result-aat: Result and advanced trace
[!WARNING] The former name of this project (
Iart) has been changed toresult-aatdue to potential trademark infringement.discussions: https://github.com/ookatuk/Iart/issues/12
or
discussions: https://github.com/ookatuk/result-aat/issues/12
[!TIP]
This Beta version.
The documentation is definitely insufficient.
a structure inspired by [Result], designed for std and no-std.
supporting event-driven handling and dynamic tracing.
While disruptive updates are possible, we intend to provide bug fix support for older versions for a certain period if they still have a significant user base.
However, since bug fixes may be discontinued without notice, we recommend checking the
README.mdfile and updating as soon as possible.
Features
- Event Notification: Automatically notifies handlers when error-handling methods are executed.
no-stdandno-allocTracing: Lightweight and simple execution tracing that works in embedded environments.- Usage Validation: Issues warnings if the result is not handled properly (goes beyond simple
is_errchecks). - works on
stableRust: See theNightly build only?section. - High utility for
stdprojects: Beyond just no-std, it provides unique safety nets—like passing unused errors to custom handlers—that you won't find in standard error-handling crates.
Except for the default handler, functionality with no-std is not restricted!
Examples(It works in stable)
use result_aat::prelude::Iart;
use result_aat::prelude::DummyErr;
use result_aat::prelude::ErrorDetail;
use result_aat::prelude::ToResultRet;
use result_aat::iart_try;
use core::panic::Location;
use std::collections::VecDeque;
// or alloc::collections::VecDeque
fn main() {
// 1. Success
let res = Iart::new_ok("hi");
// 2. Errors with diagnostic messages
// Use `Err` for static messages or `Err_string` for dynamic ones (like `format!`).
let res_err1: Iart<i32> = Iart::new_err(DummyErr {}, "Static error message");
let res_err2: Iart<u32> = Iart::new_string_err(DummyErr {}, format!("Dynamic error: {}", 404));
// or Iart<u32> = Iart::new_err(DummyErr{}, "test").with_item(56);
let mut res_err1: Iart<i32> = res_err1.ok().err().unwrap(); // ok function is if result is ok, return `i32`, if not ok, return self
let result: Iart<u32> = core::result::Result::Err(DummyErr {}).into(); // Can This!(if IartErr is included in struct, From impl supported)
let _ = result.unwrap_err();
let _ = res.unwrap();
// if you need downcast, use [`Iart::try_downcast`]
fn test() -> Iart<u32> { // Try is not supported by default, but if you want to use it...
let result: Iart<u32> = Iart::new_ok(5);
// in nightly build,
// result? is supported(`for-nightly-try-support` feature)
let res: u32 = iart_try!(result); // for stable build
// or `iart_open_no_log!` (can use in all build)
Iart::new_ok(res)
}
let res = test().unwrap();
assert_eq!(res, 5);
// Unless someone maliciously provides invalid values to the explicitly `unsafe` function [`iart_core::ErrorDetail::new`], UB will not occur in general use(and tests works).
// Conversely, it is marked `unsafe` precisely because [`iart_core::ErrorDetail::new`] results in UB if given invalid values.
//
// Besides, why should we even need to account for manual tampering with [`iart_core::ErrorDetail::new`]?
let res: Result<ToResultRet<DummyErr, u32>, Iart<u32>> = unsafe { test().to_result() }; // can this!
// This can be done even under normal circumstances.
res_err1.for_each_log(|log: &'static Location<'static>| -> bool {
println!("{:?}", log);
true
});
// 3. Automatic Warning on Drop
// If an error is dropped without being handled, result-aat automatically notifies the handler.
# unsafe { res_err1.__internal_mark_handled() };
drop(res_err1); // Triggers a warning to the handler
// 4. Proper Handling
// Methods like `unwrap_err()` mark the instance as "handled," suppressing the drop warning.
match res_err2.unwrap_err() {
(detail, _) => {
// `detail.desc` provides access to the stored diagnostic message.
println!("Handled error: {:?}", detail.desc);
}
}
}
Nightly build only?
No!
I've ensured it works perfectly(with cargo hack test) with stable builds as well!
While some syntax is limited, usability is only slightly affected.
(For example, you'll use iart_try! instead of the ? operator.)
Crucially, almost all core features are NOT restricted!
(Everything except those explicitly marked with for-nightly- feature flags.)
Please give it a try, even on your stable toolchain!
Are you worried because it's too small?
I understand that if I were in your shoes, I'd be worried too.
However,
There are many features listed here as examples, but this is not an exhaustive list. (check now features)
since it's a library containing nearly 2500 lines of code, the size is reasonable, and the contents are manageable.
If you're worried, please do check it out.
Wait? can I set max traces?
A: YES!
If you need set max, Please set env IART_TRACE_MAX=(number)(I'm using env macro(not func))
If you need select Delete type,
set IART_TRACE_TYPE=good/last/first
good- A system that caused the error is not deleted; instead, the old version that was used as an intermediary is deleted and a new version is installed.first- When a new one arrives, if the limit is reached, it will skip over the old one instead of overwriting it.last- A system where new data overwrites old data.
Runtime costs?
Did you think the runtime cost was too high?
Don't worry.
Most of the features of this structure (error tracing(allow-backtrace-logging), detection of unused errors(
check-unused-result)) can be toggled using features.
There are also features not mentioned here, so please take a look.
Regarding performance
It's at least slower than result, but
you might see improvement by turning off all features (which almost completely eliminates the cost of dropping) or by
enabling for-nightly-likely-optimization when release build!
Sometimes you want to return the same struct on failure as you do on success, for a specific reason, right?
The error-can-have-item feature comes in handy!
Of course, we welcome suggestions!
Advanced Examples(works in stable)
use result_aat::prelude::Iart;
use result_aat::prelude::DummyErr;
use result_aat::prelude::events::IartEvent;
use result_aat::prelude::events::AutoRequestType;
use result_aat::prelude::IartHandleDetails;
use result_aat::prelude::set_handler;
use result_aat::IartErr;
use core::fmt;
#[derive(
IartErr,
Debug,
Clone
)] // `IartErr` required `Send`, `Sync`, `Clone` and `Debug`, Display, (If you use generic_member_access, need [`core::error::Error`])
struct Error {}
impl core::error::Error for Error {}
impl core::fmt::Display for Error { // Important parts such as `expect` are not passed to the handler, so you need to specify them.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
// During panics, including with `no-std` builds, the allocator is not used automatically. (Probably)
//If called for reasons other than handling `fmt`, the error will be ignored.
// `Debug/Display` event may be called for formatting during a panic.
fn handler(event: IartEvent, iart: IartHandleDetails) -> core::fmt::Result {
match event {
IartEvent::DebugRequest(fmt) => {
write!(fmt, "debug fmt")?;
}
IartEvent::DroppedWithoutCheck => { // When using `std`, this method is not called during a panic.
println!("success detect!");
}
IartEvent::FunctionHook(hook_type) => {
match hook_type {
AutoRequestType::Unwrap => {
// Retrieve the last recorded location from the audit log
if let Some(location) = iart.log.and_then(|l| l.back()) {
println!("Audit: unwrap detected at {}:{}!", location.file(), location.line());
}
}
_ => {}
}
}
_ => {}
}
Ok(())
}
fn main() {
assert_eq!(set_handler(handler), true); // It will be stored atomically.
// and optional
// if you need default handler, enable `enable-default-handler`
// ...
}
Note: This structure requires alloc.
If you need to use a specific allocator instead of the global allocator, we recommend enabling the
for-nightly-allocator-api-support feature.
Want to Panic?
Use now danger-allow-panic-if-unused!
Is this using AI?
A:
Yes, But we also conduct thorough execution tests(
cargo hack testand other) and visual checks, and I'll say that I wrote 95%(I also used AI only in thetests.rssection / document (of course, I wrote some of it myself as well).) of it myself.As I've said many times,
- I check everything by reviewing it and running the code to make sure it's okay.
- The implementation does not use AI.(Except for tests/doc/comment)
- Even when using AI, we perform visual checks and verify the processing.
- Even if I were to use AI in my implementations in the future, > I would check the documentation for any unfamiliar functions before evaluating them.
Expected Use Cases
- To detect results that are suppressed or resolved with only
is_okat runtime. - To retain items even in case of errors.
- To dynamically and statically retain text in addition to error cases.
- To dynamically add errors from external sources.
- To add a backtrace to [
Result]. - To obtain an environment-independent backtrace.
- To obtain an easily understandable backtrace.
- To use it intuitively without looking at the document, relying solely on predictive text.
Features
std
Enables std library support. This allows the use of standard types like [
std::error::Error] and enables std-specific optimizations or panic behaviors.
for-nightly-likely-optimization - (nightly) Unlikely and cold_path are placed in areas where abnormal processing occurs, and the placement of paths that are normally accessed is optimized.
for-nightly-try-support - (nightly) Supports try_api_v2 and enables Iart::Err()?.
for-nightly-error-generic-member-access - (nightly)
no-trace-dedup - If there are consecutive traces from the same location, the function to not add will be disabled.
allow-backtrace-logging
When an error is logged, The source code location is recorded from the time the structure is created until, A method that destroys the structure is executed. This uses
Location::caller().
allow-backtrace-logging-with-ok - allow-backtrace-logging is also applied when the result is OK.
check-unused-result - This feature reports to a handler if an item is dropped and the error details are not handled clearly.
check-unused-result-with-ok - This feature checks check-unused-result even when the result is OK.
danger-allow-panic-if-unused - If unused is detected, panic! will be executed after the handler has finished processing.
core_error-support
It cannot coexist with
stdBecause,core_error::Errorbecomesstd::error::ErrorOtherwise,
core_error::Errorwill be implemented correctly.
enable-default-handler
stdis required.It's okay if this feature is invalid or if set_handler isn't called.
However, if unused is detected in the case of
std, it simply callseprintln!.
for-nightly-allocator-api-support - (nightly) Enables allocator-api support. Usage is the same as core, using
new_in, etc.
alloc
Supports Box instead of static references.
enable-pending-tracker
For those in situations where
Dropcannot detect them, this is probably good news. This feature allows you to retrieve currently unprocessed IARTs.You're probably thinking there must be a better way, right?
This is all I could come up with after two days.
Please forgive me.
Deleted features
error-can-have-item
It has been integrated into the main program.
It can be used with
new_err(...).with_item.Several related functions have also been modified.
old description:
`Err_item`, `Err_item_option`, etc., will be added. Please note that these will be passed in a special format. For example, in the case of the `err` method, it is passed as the second tuple.
How to install?
cargo add iart
Mini Q&A
Q: I have something to worry about.
A:
If it's about specifications, please use the Q&A section here or email me directly.
< If it's about practical use, try forking a small project and converting it into something you can do in your spare time.
Suggestions are very welcome!
Q: does Box::leak completely break it?(by reddit user)
A:
Unfortunately, that's correct. Since it's a system that triggers a sound based on 'Drop' detection, that's unfortunately how it should work.
However, if we find an improvement for the nightly feature or something similar, we plan to gradually switch to that (as a feature)!
However, if it can't be detected at runtime, it's difficult to confirm whether it can be checked at compile time, so don't get your hopes up too much.
Q: Will there be any disruptive changes?
A:
There are no plans to do so for the time being.
In the current plan, breaking changes only occur when you intentionally enable the feature flag
Q: It's so scary to use, I don't want to!(When converting to any, you might need to leak once and then convert it back
to a box using into_raw.)
A: Try the tests in all possible situations, or try different values until you feel confident.
Even so, if you have a bad feeling about the conversion, you can still use it to some extent without it, and since it passed the tests, it should be working correctly. Also, it's only used internally!
Don't worry!
Q: What about continuity?
A: we are take action if you report it.
However, the order of responses may change, or only alternative solutions may be provided. (Even in such cases, we will review the methods as much as possible and strive to avoid changes that would compromise compatibility.)
If I were to terminate the project, I would make sure to switch to read-only mode first.
Q: Is it compatible with thiserror?
A: It will work, and thiserror should fit in nicely.
Q: Is it okay if the code goes into a panic?
A: In a std environment where unwind is running,
in the event of a panic, it simply terminates without doing anything. In the case of
no-std, there is no detection method, but it should be fine because it probably doesn't usealloc.
Q: The warning when dropping is too severe.
A: I would appreciate it if you could either disable the feature or come up with some suggestions.
Q: I'm scared of macro dependencies.
A: You can either acquire the regex skill or try using the following code:
let res = match xxx.is_ok
Q: I'll use it as a library, but I don't want to release it externally.
A: Then let's use to_result.(A cast from dyn to the type specified in the argument is performed.)
Q: There's an unsafe method, but is it safe?
A:
It's safe to use unless you perform some kind of black magic like crafting Iart via [
iart_core::ErrorDetail::new].
UBonly occurs when two different types are involved.
Q: why 0.x.x?(Unfortunately, this may continue during the 1.0.x period.)
A:
The bug detection rate differs between individual use and use by many people, so future fixes are possible.
However, the fact remains that it was working with
no-std, so the amount of fixes needed should be small compared to other projects.
Q: Can you even be trusted in the first place?
A:
If you look at my GitHub contributions, you'll see that I've worked on multilingual support, bug fixes, and feature additions (and had them merged) in languages other than Rust.
If you're interested in Rust(and memory safety), you can see it in the code of my projects(pinned). (My documentation might make you doubt my abilities, though.)
Q: LTS?
A:
I intend to create it if it is used by many people or if there is a request.
However, for updates like this one (1.x -> 2.x), I intend to continue supporting fixes for older versions for a certain period of time.
Q: Why did you update to 2.0 when you explicitly said you wouldn't?
A:
Yes...well, I felt there were more problems than I expected, especially serious issues like
Errnot being anenum.After refactoring those issues, this is the result of my plan.
I'm certainly reflecting on this myself, so although 1.x is not an LTS (Long-Term Support) version, I intend to only make fixes without API changes for a long period of time (though this is undecided).
That said, if 2.0 still proves to be problematic, I might have to take the same approach again (by moving to a new major version or providing similar long-term fixes). I'll handle it as the situation demands.
Q: What changed from 1.0 to 2.0?
A:
I think it's designed to reduce the amount.
Err(),Ok(),Err_string(), etc., have been renamed tonew_err,new_ok,new_string_err, etc.Err_string_itemandErr_itemhave been changed to builder patterns such asnew_err(...).with_itemandnew_string_err(...).with_item.spinhas been changed fromdev-dependenciestodependencies. (Currently, this only applies tospin-mutex,once, andmutex,lazu.)- Some of the return values have been changed to structures.
- Also, please note that the types of return values for
errandunwrap_errhave changed.