result-aat 2.0.0-beta.4

The Infomation and Result Trace crate
Documentation

result-aat: Result and advanced trace

Downloads Crates.io Docs.rs License

[!WARNING] The former name of this project (Iart) has been changed to result-aat due 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.md file and updating as soon as possible.

Features

  1. Event Notification: Automatically notifies handlers when error-handling methods are executed.
  2. no-std and no-alloc Tracing: Lightweight and simple execution tracing that works in embedded environments.
  3. Usage Validation: Issues warnings if the result is not handled properly (goes beyond simple is_err checks).
  4. works on stable Rust: See the Nightly build only? section.
  5. High utility for std projects: 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);
        }
    }
}

see examples

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 test and other) and visual checks, and I'll say that I wrote 95%(I also used AI only in the tests.rs section / document (of course, I wrote some of it myself as well).) of it myself.

As I've said many times,

  1. I check everything by reviewing it and running the code to make sure it's okay.
  2. The implementation does not use AI.(Except for tests/doc/comment)
  3. Even when using AI, we perform visual checks and verify the processing.
  4. 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

  1. To detect results that are suppressed or resolved with only is_ok at runtime.
  2. To retain items even in case of errors.
  3. To dynamically and statically retain text in addition to error cases.
  4. To dynamically add errors from external sources.
  5. To add a backtrace to [Result].
  6. To obtain an environment-independent backtrace.
  7. To obtain an easily understandable backtrace.
  8. 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 std Because, core_error::Error becomes std::error::Error

Otherwise, core_error::Error will be implemented correctly.

enable-default-handler

std is 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 calls eprintln!.

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 Drop cannot 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 use alloc.

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].

UB only 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 Err not being an enum.

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.

  1. Err(), Ok(), Err_string(), etc., have been renamed to new_err, new_ok, new_string_err, etc.
  2. Err_string_item and Err_item have been changed to builder patterns such as new_err(...).with_item and new_string_err(...).with_item.
  3. spin has been changed from dev-dependencies to dependencies. (Currently, this only applies to spin-mutex, once, and mutex, lazu.)
  4. Some of the return values have been changed to structures.
  5. Also, please note that the types of return values for err and unwrap_err have changed.