Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ All significant changes to this project will be documented in this file.

### New Features

* `Exn<E>` now implements `.into_error()`, allowing to recover the top-level error with move semantics.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be in the section of Unreleased.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whoops, sorry.. I've made a PR #49 since this one is already merged

* `Exn<E>` now implements `Deref<Target = E>`, allowing for more ergonomic access to the inner error.
* This crate is now `no_std` compatible, while the `alloc` crate is still required for heap allocations. It is worth noting that `no_std` support is a nice-to-have feature, and can be dropped if it blocks other important features in the future. Before 1.0, once `exn` APIs settle down, the decision on whether to keep `no_std` as a promise will be finalized.
* `Frame` now implements `std::error::Error`.
Expand Down
4 changes: 4 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ path = "src/custom-layout.rs"
name = "downcast"
path = "src/downcast.rs"

[[example]]
name = "into-error"
path = "src/into-error.rs"

[[example]]
name = "into-anyhow"
path = "src/into-anyhow.rs"
Expand Down
104 changes: 104 additions & 0 deletions examples/src/into-error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright 2025 FastLabs Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! # Into Error example - recovering without `Clone`
//!
//! This example shows how to retrieve the internal error type

use std::error::Error;

use derive_more::Display;
use exn::Result;
use exn::ResultExt;

fn main() -> Result<(), MainError> {
app::run("what is my age?".to_string()).or_raise(|| MainError)?;
app::run("what is the answer?".to_string()).or_raise(|| MainError)?;
app::run("who am I?".to_string()).or_raise(|| MainError)?;
Ok(())
}

#[derive(Debug, Display)]
#[display("fatal error occurred in application")]
struct MainError;
impl std::error::Error for MainError {}

mod app {
use super::*;

pub fn run(question: String) -> Result<u64, AppError> {
match human::answer(question) {
Err(e) => {
if e.is_partial() {
Ok(e.into_error().partial_data())
} else {
Err(e.raise(AppError))
}
}
Ok(v) => Ok(v),
}
}

#[derive(Debug, Display)]
#[display("could not resolve answer")]
pub struct AppError;
impl std::error::Error for AppError {}
}

mod human {
use exn::bail;

use super::*;

pub fn answer(question: String) -> Result<u64, HumanError> {
if question == "what is my age?" {
return Ok(23);
} else if question == "what is the answer?" {
bail!(HumanError::Partial(42))
}
bail!(HumanError::Fatal { question })
}

#[derive(Debug, Display, PartialEq, Eq)]
pub enum HumanError {
#[display("unanswerable question asked: {question}")]
Fatal {
question: String,
},
Partial(u64),
}

impl HumanError {
pub fn is_partial(&self) -> bool {
matches!(self, HumanError::Partial(_))
}

pub fn partial_data(self) -> u64 {
match self {
HumanError::Partial(v) => v,
_ => panic!(),
}
}
}

impl Error for HumanError {}
}

// Output when running `cargo run --example into-error`:
//
// Error: fatal error occurred in application, at examples/src/into-error.rs:28:39
// |
// |-> could not resolve answer, at examples/src/into-error.rs:46:27
// |
// |-> unanswerable question asked: who am I?, at examples/src/into-error.rs:70:9
5 changes: 5 additions & 0 deletions exn/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ impl<E: Error + Send + Sync + 'static> Exn<E> {
pub fn frame(&self) -> &Frame {
&self.frame
}

/// Extract the top-level error using move semantics
pub fn into_error(self) -> E {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may deserve a CHANGELOG entry.

@feefladder Either you can update CHANGELOG.md or I can merge it and update in a follow-up PR.

*self.frame.error.downcast().expect("error type must match")
}
}

impl<E> Deref for Exn<E>
Expand Down