Iart: Is Advanced Result Trace
a structure inspired by [Result], designed for std and no-std.
supporting event-driven handling and dynamic tracing.
Incidentally, I took a library I was already using personally, turned it into a crate, added features, and stabilized the specifications at the time of the initial release.
If we were to change the specifications, it would be in version 1.0 or 2.0.
Features
- Event Notification: Automatically notifies handlers when error-handling methods are executed.
no-stdTracing: 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.
Perfect for projects expecting no-std support!
Except for the default handler, functionality with no-std is not restricted!
Examples(It works in stable)
use iart::prelude::Iart;
use iart::prelude::DummyErr;
use iart::prelude::ErrorDetail;
use iart::iart_try;
use core::panic::Location;
use std::collections::VecDeque;
// or alloc::collections::VecDeque
fn main() {
// 1. Success
let res = Iart::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::Err(DummyErr {}, "Static error message"); // **NOT Enum! This is function!**
let res_err2: Iart<u32> = Iart::Err_string(DummyErr {}, format!("Dynamic error: {}", 404));
// or Iart<u32> = Iart::Err_item(DummyErr{}, "test", 56); // `error-can-have-item`
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::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::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<(Result<(), (DummyErr, Box<ErrorDetail>)>, Option<u32>, Option<VecDeque<&'static Location<'static>>>), 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, iart 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 iart::prelude::Iart;
use iart::prelude::DummyErr;
use iart::prelude::events::IartEvent;
use iart::prelude::events::AutoRequestType;
use iart::prelude::IartHandleDetails;
use iart::prelude::set_handler;
use iart::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() {
set_handler(handler); // 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)
- 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
This enables the standard format of the dependent crate.
It is also used as a criterion for determining whether something is standard, It is also used for other purposes, such as enabling/disabling panic checks during
stdbuilds.
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-loggingis 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 checkscheck-unused-resulteven when the result is OK.danger-allow-panic-if-unused- If unused is detected,panic!will be executed after the handler has finished processing.error-can-have-itemErr_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 theerrmethod, it is passed as the second tuple.core_error-supportIt cannot coexist withstdBecause,core_error::Errorbecomesstd::error::ErrorOtherwise,
core_error::Errorwill be implemented correctly.enable-default-handlerstdis 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) Enablesallocator-apisupport. Usage is the same ascore, usingnew_in, etc.
Todo Features
no-alloc - It would be great if we could do this without even using alloc. However, Box might need to be made &'static'.
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() {
Ok(item) => {
res
}
Err(err) => {
err.send_log();
return err;
}
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?
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(pined). (My documentation might make you doubt my abilities, though.)