Rust wrapping io::Errror and threads

Hi I am trying to refactor some code I wrote quite a while ago but having the following issue.

help: within std::result::Result<(std::boxed::Box<dyn config::Point>, std::time::SystemTime, f64, u64), error::YosemiteError>, the trait std::marker::Send is not implemented for std::rc::Rc<std::io::Error>

My Error enum is defined like

 #[derive(Debug, Clone)]
pub enum YosemiteError {
    GeneralError(String),
    Io(Rc<std::io::Error>),
    TimeoutError(Rc<tokio::time::Elapsed>, String, u16, usize),
    IpPortCommError(Rc<std::io::Error>, String, u16, usize),
    PointIoError(Rc<std::io::Error>, Box<dyn Point>, String),
    PointError(Box<dyn Point>, String),
    UnitError(String),
    SerdeError(Rc<serde_json::Error>),
    AddrError(std::net::AddrParseError),
}

Not sure how it worked before but the error seems to make sense Rcstd::io::Error is not Send so I want to proceed with correcting this.
std::io::Error is not clone which is why I had the Rc I believe. How should I define an Error that can be returned from threads keeping the original io error ?
Not sure what is the best practice. Should I just convert the io error to String or something ?

Thanks

Why do you want to clone errors?

I was saving all the errors is a list. They get processed and reported to user at a later time. I could perhaps try to remove that though.

In similar situations, I've defined a custom structure to handle the type erasure instead of relying solely on Rust's dyn machinery. For errors, it might look something like this:

use std::error::Error;
use std::fmt::{Display, Formatter};

#[derive(Debug,Clone)]
struct NormalizedError {
    source: Option<Box<Self>>,
    desc: String,
}

impl<E:Error + ?Sized> From<&E> for NormalizedError {
    fn from(err:&E)->Self {
        NormalizedError {
            source: err.source().map(|x| Box::new(x.into())),
            desc: format!("{err}")
        } 
    }
}

impl Error for NormalizedError {
    fn source(&self)->Option<&(dyn Error+'static)> {
        self.source.as_ref().map(|x| &**x as &dyn Error)
    }
    
    fn description(&self)->&str {
        self.desc.as_str()
    }

    fn cause(&self)->Option<&dyn Error> {
        self.source.as_ref().map(|x| &**x as &dyn Error)
    }
}

impl Display for NormalizedError {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
        write!(f, "{}", self.desc)
    }
}
1 Like

That looks nice Thanks.
I will try to implement that. Out of curiosity how can you impl From for str

like

impl From<&str> for NormalizedError {
    fn from(err: &str) -> NormalizedError {
        NormalizedError {
            source: None,
            desc: format!("{err}")
        } 
    }
}

without getting

conflicting implementations of trait std::convert::From<&str> for type error::NormalizedError

As far as the compiler is concerned, std might one day implement the Error trait for str. If that happens, there would be no way for the compiler to choose between them. In practice, you can rarely mix generic From implementations with ones for specific types. Instead, one or the other needs to be written as either an inherent method or a different trait.

You could, for example, implement FromStr:

impl FromStr for NormalizedError {
    type Err = std::convert::Infallible;
    fn from_str(err:&str)->Result<Self, std::convert::Infallible> {
        Ok(NormalizedError {
            source: None,
            desc: format!("{err}")
        })
    }
}

(untested)

Here's how to make your type Send:

  • Use Arc instead of Rc (same thing but with a thread-safe reference counter).
  • Make sure each dyn includes Send + Sync (if the trait itself doesn't).
use std::sync::Arc;

#[derive(Debug, Clone)]
pub enum YosemiteError {
    GeneralError(String),
    Io(Arc<std::io::Error>),
    TimeoutError(Arc<tokio::time::error::Elapsed>, String, u16, usize),
    IpPortCommError(Arc<std::io::Error>, String, u16, usize),
    PointIoError(Arc<std::io::Error>, Arc<dyn Point + Send + Sync>, String),
    PointError(Arc<dyn Point + Send + Sync>, String),
    UnitError(String),
    SerdeError(Arc<serde_json::Error>),
    AddrError(std::net::AddrParseError),
}

Playground

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.