Support errors with lifetimes to have a source - error-handling

I have the following 3 errors, AError, BError and CError, where CError contains a borrowed string from some input, and AError is composed from either BError or CError, and thus has a lifetime too.
use std::error::Error;
use std::fmt;
#[derive(Debug)]
pub enum AError<'a> {
B(BError),
C(CError<'a>),
}
impl<'a> fmt::Display for AError<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "AError")
}
}
impl<'a> Error for AError<'a> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
AError::B(err) => Some(err),
AError::C(_) => None, // If I try to use `C(err) => Some(err)` it errors
}
}
}
#[derive(Debug)]
pub struct BError;
impl fmt::Display for BError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "BError")
}
}
impl Error for BError {}
#[derive(Debug)]
pub struct CError<'a> {
c: &'a str,
}
impl<'a> fmt::Display for CError<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CError: {}", self.c)
}
}
impl<'a> Error for CError<'a> {}
If I try to make A return C as a source error, I get the following error:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/lib.rs:18:15
|
18 | match self {
| ^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 16:6...
--> src/lib.rs:16:6
|
16 | impl<'a> Error for AError<'a> {
| ^^
note: ...so that the types are compatible
--> src/lib.rs:18:15
|
18 | match self {
| ^^^^
= note: expected `&AError<'_>`
found `&AError<'a>`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable
--> src/lib.rs:18:9
|
18 | / match self {
19 | | AError::B(err) => Some(err),
20 | | AError::C(err) => Some(err),
21 | | }
| |_________^
= note: expected `Option<&(dyn std::error::Error + 'static)>`
found `Option<&dyn std::error::Error>`
Which makes sense as source is declared with fn source(&self) -> Option<&(dyn Error + 'static)>.
I understand in order to be able to downcast, you must have a static reference, but all I want to do with it is print a backtrace of errors using source as such:
pub fn fmt_err(err: &dyn Error, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "{}", err)?;
if let Some(source) = err.source() {
fmt_err(source, f)?;
}
Ok(())
}
which doesn't require downcasting, simply uses the fact that fmt::Display is a super-trait of Error.
How can I make my errors with lifetimes support this type of usecase?
I know Error::cause exists, but it is deprecated and likely not intended for this use case and popular error deriving libraries (such as thiserror) likely don't implement it either, so I have to make do with source as my only way of getting an underlying error.
Is my only option to make my own trait and implement it for any errors with lifetimes?

Related

How do I transform io::Result into anyhow::Result?

I'm starting with this working code:
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader, Result};
use std::path::Path;
fn read_lines<P, I: IntoIterator<Item = P>>(files: I) -> impl Iterator<Item = Result<String>>
where
P: AsRef<Path>,
{
let handles = files.into_iter().map(|path| File::open(path).unwrap());
handles.flat_map(|handle| BufReader::new(handle).lines())
}
fn main() -> Result<()> {
let lines = read_lines(env::args().skip(1).collect::<Vec<String>>());
for line in lines {
println!("{:?}", line?)
}
Ok(())
}
I need to integrate this into a codebase that is heavily reliant on the anyhow library, but I have no idea how I can massage the BufReader::lines return value inside the flatmap into a impl Iterator<Item = anyhow::Result<String>>.
As a reproducible example of where I'm stuck, I integrate anyhow into my test bed with this Cargo.toml,
[package]
name = "rust-playground"
version = "0.1.0"
authors = ["Charles"]
edition = "2018"
[dependencies]
anyhow = "1"
And I replace the import of std::io::Result with anyhow::Result. I'm unsure where to place with_context calls, everything I've tried has led to compiler errors.
This attempt fails because I can't use ? inside the closure, but how else can I "unwrap"? I'm not allowed to use unwrap in this context, I'm expected to return an anyhow::Result, somehow...
use anyhow::{Context, Result};
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
fn read_lines<P, I: IntoIterator<Item = P>>(files: I) -> impl Iterator<Item = Result<String>>
where
P: AsRef<Path>,
{
let handles = files.into_iter().map(|path| {
File::open(path).with_context(|| format!("opening path: {}", path.as_ref().display()))?
});
handles.flat_map(|handle| BufReader::new(handle).lines())
}
fn main() -> Result<()> {
let lines = read_lines(env::args().skip(1).collect::<Vec<String>>());
for line in lines {
println!("{:?}", line?)
}
Ok(())
}
And the error message:
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `Try`)
--> src/main.rs:13:10
|
12 | files.into_iter().map(|path|
| ___________________________-
13 | | File::open(path).with_context(|| format!("opening path: {}", path.as_ref().display()))?);
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
| |___________________|_____________________________________________________________________________________|
| | this function should return `Result` or `Option` to accept `?`
| cannot use the `?` operator in a closure that returns `File`
|
= help: the trait `Try` is not implemented for `File`
= note: required by `from_error`
error[E0271]: type mismatch resolving `<FlatMap<Map<<I as IntoIterator>::IntoIter, [closure#src/main.rs:12:24: 13:97]>, std::io::Lines<BufReader<File>>, [closure#src/main.rs:14:22: 14:61]> as Iterator>::Item == std::result::Result<String, anyhow::Error>`
--> src/main.rs:7:58
|
7 | fn read_lines<P, I: IntoIterator<Item = P>>(files: I) -> impl Iterator<Item = Result<String>>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::io::Error`, found struct `anyhow::Error`
|
= note: expected enum `std::result::Result<_, std::io::Error>`
found enum `std::result::Result<_, anyhow::Error>`
I can figure out a way to make this compile if my method handles on a single filename:
fn read_lines<P>(filename: P) -> Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(&filename)
.with_context(|| format!("opening filename: {}", filename.as_ref().display()))?;
Ok(BufReader::new(file).lines())
}
But this doesn't generalize properly to the the case of handling multiple filenames, since File::open(&path).with_context(...)? is not correct inside the iterator.
The last snippet you provided for handling single files doesn't seem to be returning the same thing as the previous examples.
If you want to just extend the single file example for multiple files, then you could just map the single-file function over the list of files.
Something like this
fn read_lines<P, I: IntoIterator<Item = P>>(files: I) -> impl Iterator<Item = Result<std::io::Lines<BufReader<File>>>>
where
P: AsRef<Path>,
{
files.into_iter().map(|filename: P| {
let file = File::open(&filename)
.with_context(|| format!("opening filename: {}", filename.as_ref().display()))?;
Ok(BufReader::new(file).lines())
})
}
But if you would like the function to return a Result<String> instead of a Result<std::io::Lines<BufReader<File>>>, you can transform the errors from io::Error to anyhow::Error like so,
fn read_lines<P, I: IntoIterator<Item = P>>(files: I) -> impl Iterator<Item = Result<String>>
where
P: AsRef<Path>,
{
files.into_iter().flat_map(|filename: P| {
let file = File::open(&filename)
.with_context(|| format!("opening filename: {}", filename.as_ref().display()));
file.into_iter().flat_map(|file| {
BufReader::new(file)
.lines()
.map(|line| line.map_err(anyhow::Error::from))
})
})
}

How to destructure enum when de/serializing yaml or json with serde?

I have a piece of serde code which does what I want, but I don't like how it does it. I'm looking for help with figuring out on how to improve it.
Playground
use std::any::Any;
trait Model: std::fmt::Debug + Any {
fn as_any(&self) -> &dyn Any;
}
impl Model for Generator {
fn as_any(&self) -> &dyn Any {
self
}
}
impl Model for Connector {
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Generator {
id: String,
#[serde(rename = "sourceID")]
source_id: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Connector {
id: String,
#[serde(rename = "sourceID")]
source_id: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(tag = "type")]
enum AllModels {
Generator(Generator),
Connector(Connector),
}
fn main() {
let data = r#"
- sourceID: "generator-01"
id: "connector-01"
type: "Generator"
- sourceID: "geneiator-01"
type: "Connector"
id: "connector-01"
"#;
let p: Vec<Box<dyn Model>> = serde_yaml::from_str::<Vec<AllModels>>(&data)
.unwrap()
.into_iter()
.collect();
println!("{:?}", p);
let l = serde_yaml::to_string(&p.into_iter().collect::<Vec<AllModels>>());
println!("{:?}", l);
}
impl std::iter::FromIterator<AllModels> for Vec<Box<dyn Model>> {
fn from_iter<I: IntoIterator<Item = AllModels>>(iter: I) -> Self {
let mut v: Vec<Box<dyn Model>> = Vec::new();
for i in iter {
match i {
AllModels::Generator(d) => {
v.push(Box::new(d));
}
AllModels::Connector(d) => {
v.push(Box::new(d));
}
}
}
v
}
}
impl std::iter::FromIterator<std::boxed::Box<dyn Model>> for std::vec::Vec<AllModels> {
fn from_iter<I: IntoIterator<Item = Box<dyn Model>>>(iter: I) -> Self {
let mut v: Vec<AllModels> = Vec::new();
for i in iter {
if let Some(model) = i.as_any().downcast_ref::<Generator>() {
v.push(AllModels::Generator(model.clone()));
} else if let Some(model) = i.as_any().downcast_ref::<Connector>() {
v.push(AllModels::Connector(model.clone()));
}
}
v
}
}
What I'm trying to achieve is to de/serialize yaml into one of multiple structs, dynamically choosing to which struct should it deserialize to based on value of type field in yaml it parses. e.g.
- id: Foo
source: Bar
type: Connector
should be parsed into struct Connector
I figured I could use enum representation to deal with that, however, it produces undesired side effect - by default following yaml:
- id: Foo
source: Bar
type: Connector
- id: Foo
source: Bar
type: Generator
will be parsed as:
[Connector(Connector{...}), Generator(Generator{...})]
so my structs are wrapped in enum variants. In order to "unwrap it" I figured I could implement FromIterator<AllModels> for Vec<Box<dyn Model>> , thanks to which and type conversion/coercion(not sure which one is the right word) the output changes to:
[Connector{...}, Generator{...}]
so far so good.
Two issues I'm having with this solution, are:
code repetition - for each new struct (Connector,Generator,...) I have to update enum AllModels and match arm inside FromIterator implementation - the latter is what bothers me the most. I could do it with macro probably, but I haven't learned how to write them, and before I do so, I'd like to explore other possible solutions
extra iteration - in order to convert from Vec<enum variant> to Vec<struct> I need to do the following: let p: Vec<Box<dyn Model>> = serde_yaml::from_str::<Vec<AllModels>>(&data).unwrap().into_iter().collect();. I'd prefer if the conversion would happen without an extra iteration
I have considered a few of options, but I'm not able to figure how to implement them...
A. serde container attribute from/into docs
#[serde(from = "FromType")] - the way I think it would work is by force-converting my enum variant straight into desired struct, with no extra iteration and no code repetition. However, I fail to implement it - Playground.
When I'm trying to add from attribute
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
enum AllModels {
Gene(Generator),
Connector(Connector),
}
the compiler will yell at me
❯ cargo r
Compiling serdeissue v0.1.0 (/sandbox/serdeissue)
error[E0277]: the trait bound `Box<dyn Model>: From<AllModels>` is not satisfied
--> src/main.rs:21:24
|
21 | #[derive(Debug, Clone, Serialize, Deserialize)]
| ^^^^^^^^^ the trait `From<AllModels>` is not implemented for `Box<dyn Model>`
|
= help: the following implementations were found:
<Box<(dyn StdError + 'a)> as From<E>>
<Box<(dyn StdError + 'static)> as From<&str>>
<Box<(dyn StdError + 'static)> as From<Cow<'a, str>>>
<Box<(dyn StdError + 'static)> as From<std::string::String>>
and 22 others
= note: required because of the requirements on the impl of `Into<Box<dyn Model>>` for `AllModels`
= note: required by `into`
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `dyn Model: Serialize` is not satisfied
--> src/main.rs:21:24
|
21 | #[derive(Debug, Clone, Serialize, Deserialize)]
| ^^^^^^^^^ the trait `Serialize` is not implemented for `dyn Model`
|
::: /home/marcin/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.123/src/ser/mod.rs:247:18
|
247 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
| - required by this bound in `serialize`
|
= note: required because of the requirements on the impl of `Serialize` for `Box<dyn Model>`
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `dyn Model: Deserialize<'_>` is not satisfied
--> src/main.rs:21:35
|
21 | #[derive(Debug, Clone, Serialize, Deserialize)]
| ^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `dyn Model`
|
::: /home/marcin/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.123/src/de/mod.rs:539:12
|
539 | D: Deserializer<'de>;
| ----------------- required by this bound in `_::_serde::Deserialize::deserialize`
|
= note: required because of the requirements on the impl of `Deserialize<'_>` for `Box<dyn Model>`
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `AllModels: From<Box<dyn Model>>` is not satisfied
--> src/main.rs:21:35
|
21 | #[derive(Debug, Clone, Serialize, Deserialize)]
| ^^^^^^^^^^^ the trait `From<Box<dyn Model>>` is not implemented for `AllModels`
|
::: /home/marcin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/mod.rs:372:1
|
372 | pub trait From<T>: Sized {
| ------------------------ required by this bound in `From`
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
My angle of attack is following: use error msg to copy-pasteroni-dummy-implementoni missing trait bounds:
impl From<AllModels> for Box<dyn Model> {
fn from(am: AllModels) -> Self {
Box::new(Generator{id:String::from("arst"),source_id:String::from("arst")})
}
}
impl Serialize for dyn Model {
fn serialize(&self) -> Self {
Box::new(Generator{id:String::from("arst"),source_id:String::from("arst")})
}
}
impl Deserialize<'_> for dyn Model {}
impl From<Box<dyn Model>> for AllModels {
fn from(dm: Box<dyn Model>) -> Self {
AllModels::Gene(Generator{id:String::from("arst"),source_id:String::from("arst")})
}
}
but then this happens:
❯ cargo r
Compiling serdeissue v0.1.0 (/sandbox/serdeissue)
error[E0277]: the size for values of type `(dyn Model + 'static)` cannot be known at compilation time
--> src/main.rs:75:6
|
75 | impl Deserialize<'_> for dyn Model {}
| ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
::: /home/marcin/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.123/src/de/mod.rs:530:29
|
530 | pub trait Deserialize<'de>: Sized {
| ----- required by this bound in `Deserialize`
|
= help: the trait `Sized` is not implemented for `(dyn Model + 'static)`
and that's a game over for me
B. erased-serde
this seems to be the right tool for the job, but again, I run into problems when implementing it (no wonder - I have no idea what I'm doing:)
use erased_serde::{Deserializer, Serializer, serialize_trait_object};
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Generator {
id: String,
source: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Connector {
id: String,
source: String,
}
trait Model: std::fmt::Debug + erased_serde::Serialize {}
erased_serde::serialize_trait_object!(Model);
impl Model for Generator {}
impl Model for Connector {}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", into = "Box<dyn Model>", from = "Box<dyn Model>")]
enum AllModels {
Generator(Generator),
Connector(Connector),
}
fn main() {
let data = r#"
- source: "generator-01"
id: "g-01"
type: "Generator"
- source: "connector-01"
type: "Connector"
id: "c-01"
"#;
let p: Vec<Box<dyn Model>> = serde_yaml::from_str(&data).unwrap();
println!("{:?}", p);
}
impl From<AllModels> for Box<dyn Model> {
fn from(am: AllModels) -> Self {
Box::new(Generator{id:String::from("arst"),source_id:String::from("arst")})
}
}
// impl Serialize for dyn Model {
// fn serialize(&self) -> Self {
// Box::new(Generator{id:String::from("arst"),source_id:String::from("arst")})
// }
// }
impl Deserialize<'_> for dyn Model {}
impl From<Box<dyn Model>> for AllModels {
fn from(dm: Box<dyn Model>) -> Self {
AllModels::Generator(Generator{id:String::from("arst"),source_id:String::from("arst")})
}
}
// impl std::convert::From<AllModels> for Box<dyn Model> {
// fn from(m: AllModels) -> Self {
// Box::new(Generator {source_id: String::from("i"), id: String::from("r")})
// }
// }
I get this error when compiling:
❯ cargo r
Compiling serdeissue v0.1.0 (/sandbox/serdeissue)
warning: unused imports: `Deserializer`, `Serializer`, `serialize_trait_object`
--> src/main.rs:1:20
|
1 | use erased_serde::{Deserializer, Serializer, serialize_trait_object};
| ^^^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
error[E0277]: the size for values of type `(dyn Model + 'static)` cannot be known at compilation time
--> src/main.rs:76:6
|
76 | impl Deserialize<'_> for dyn Model {}
| ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
::: /home/marcin/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.123/src/de/mod.rs:530:29
|
530 | pub trait Deserialize<'de>: Sized {
| ----- required by this bound in `Deserialize`
|
= help: the trait `Sized` is not implemented for `(dyn Model + 'static)`
which is something I thought erased_serde could help with, and maybe it does, but I have no clue how to implement it.
Sadly I can't use typetag crate since it doesn't support wasm compilation target which I need. I am not considering using #[serde(serialize_with = "path")] for each enum variant, since it makes my issue #1 much worse than it currently is.
I'm also aware of this question How to implement `serde::Serialize` for a boxed trait object? however the code provided by #dtolnay doesn't compile and I don't know how to fix it
❯ cargo r
Compiling serdeissue v0.1.0 (/sandbox/serdeissue)
error[E0603]: module `export` is private
--> src/main.rs:168:10
|
168 | #[derive(Serialize)]
| ^^^^^^^^^ private module
|
note: the module `export` is defined here
--> /home/marcin/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.123/src/lib.rs:275:5
|
275 | use self::__private as export;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0603]: module `export` is private
--> src/main.rs:173:10
|
173 | #[derive(Serialize)]
| ^^^^^^^^^ private module
|
note: the module `export` is defined here
--> /home/marcin/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.123/src/lib.rs:275:5
|
275 | use self::__private as export;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0603]: module `export` is private
--> src/main.rs:179:10
|
179 | #[derive(Serialize)]
| ^^^^^^^^^ private module
|
note: the module `export` is defined here
--> /home/marcin/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.123/src/lib.rs:275:5
|
275 | use self::__private as export;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0603]: module `export` is private
--> src/main.rs:184:10
|
184 | #[derive(Serialize)]
| ^^^^^^^^^ private module
|
note: the module `export` is defined here
--> /home/marcin/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.123/src/lib.rs:275:5
|
275 | use self::__private as export;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
warning: trait objects without an explicit `dyn` are deprecated
--> src/main.rs:176:22
|
176 | widgets: Vec<Box<WidgetTrait>>,
| ^^^^^^^^^^^ help: use `dyn`: `dyn WidgetTrait`
|
= note: `#[warn(bare_trait_objects)]` on by default
It looks like the feature I'm looking for is waiting to be implemented here: https://github.com/serde-rs/serde/issues/1402
There is also this issue https://github.com/serde-rs/serde/issues/1350 which suggests manual Deserializer implementation Playground. The playground code indicates this could help with my issue #2 "extra iteration", however there's still some repetition involved in code implementation, therefore I'm still looking for a better answer.
Edit: I'm also considering Enum or trait object, can't figure what's the right approach for evaluating whether I need one or the other.
I found a solution which satisfies me:
slick
no code repetition
no dynamic dispatch, no extra iterations
uses enum and serde(tag="...")
type field can be out of order in json/yaml (doesn't have to be first)
type field is included when serializing back to json/yaml
The trick was to use enum_dispatch.
use serde::{Serialize, Deserialize};
use enum_dispatch::enum_dispatch;
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Generator {
source_id: String,
id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Gate {
source_id: String,
id: String,
}
#[enum_dispatch]
trait Model {fn time_advance(self, a:i32,b:i32) -> i32;}
impl Model for Generator { fn time_advance(self,a:i32,b:i32) -> i32 {a+b} }
impl Model for Gate { fn time_advance(self,a:i32,b:i32) -> i32 {a+b} }
#[enum_dispatch(Model)]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
enum ModelTypes {
Generator(Generator),
Gate(Gate),
}
fn main() {
let s = r#"
- source_id: "generator-01"
id: "connector-01"
type: "Generator"
- source_id: "geneiator-01"
type: "Gate"
id: "connector-01"
"#;
let data: Vec<ModelTypes> = serde_yaml::from_str(s).unwrap();
println!("{:?}", serde_yaml::to_string(&data));
for d in data {
println!("{:?}", d.time_advance(4, 2));
}
}
[package]
name = "enum_unpack"
version = "0.1.0"
authors = ["---"]
edition = "2018"
[dependencies]
serde = { version = "1.0.124", features = ["derive"] }
serde_yaml = "0.8.1"
enum_dispatch = "0.3.5"
output:
> cargo run
Compiling enum_unpack v0.1.0 (.../enum_unpack)
Finished dev [unoptimized + debuginfo] target(s) in 0.66s
Running `target/debug/enum_unpack`
Ok("---\n- type: Generator\n source_id: generator-01\n id: connector-01\n- type: Gate\n source_id: geneiator-01\n id: connector-01\n")
6
6

How to split an implementation of a trait into multiple files?

I started working with ws, and I would like to split the Handler trait implementation into multiple files.
So I wrote this in one file, on_open.rs:
impl Handler for Client {
fn on_open(&mut self, _: Handshake) -> Result<()> {
println!("Socket opened");
Ok(())
}
}
And this in another file, on_message.rs:
impl Handler for Client {
fn on_message(&mut self, msg: Message) -> Result<()> {
println!("Server got message '{}'. ", msg);
Ok(())
}
}
While compiling it I got the following error:
error[E0119]: conflicting implementations of trait `ws::handler::Handler` for type `models::client::Client`:
--> src\sockets\on_message.rs:9:1
|
9 | impl Handler for Client {
| ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `models::client::Client`
|
::: src\sockets\on_open.rs:8:1
|
8 | impl Handler for Client {
| ----------------------- first implementation here
I'd like to have the files to be separated so that each developer can work on a separate one. Is there a way to achieve this or am I forced to have the full trait implementation in a single file?
Although you can have multiple impl blocks for the same object, you can't have two which are exactly the same, hence the error of conflicting implementations indicated by E0119:
Since a trait cannot be implemented multiple times, this is an error.
(If the trait could be specialised because it takes any number of generic type arguments the situation would be very much different because every specialisation would be a different impl block. However even then you wouldn't be able to have the same specialisation implemented more than once.)
If you would like to split the functionality into separate files, you could do that, but in a slightly different way than you originally thought. You could split the Client's impl block instead of the Handler implementation as the following minimal and compilable example demonstrates. (Try it in the playground!)
As you can see, the Handler trait is implemented for Client in one place, but all the implementations of Client are split into multiple files/modules and the Handler implementation is just referencing those:
mod handler
{
pub type Result<T> = ::std::result::Result<T, HandlerError>;
pub struct HandlerError;
pub trait Handler
{
fn on_open(&mut self, h: usize) -> Result<()>;
fn on_message(&mut self, m: bool) -> Result<()>;
}
}
mod client
{
use super::handler::{ self, Handler };
struct Client
{
h: usize,
m: bool,
}
impl Handler for Client
{
fn on_open(&mut self, h: usize) -> handler::Result<()>
{
self.handle_on_open(h)
}
fn on_message(&mut self, m: bool) -> handler::Result<()>
{
self.handle_on_message(m)
}
}
mod open
{
use super::super::handler;
use super::Client;
impl Client
{
pub fn handle_on_open(&mut self, h: usize) -> handler::Result<()>
{
self.h = h;
Ok(())
}
}
}
mod message
{
use super::super::handler;
use super::Client;
impl Client
{
pub fn handle_on_message(&mut self, m: bool) -> handler::Result<()>
{
self.m = m;
Ok(())
}
}
}
}
Thanks for #Peter's answer, I re-wrote my code as below, and it is working fine:
socket.rs
use ws::Handler;
use crate::models::client::Client;
use ws::{Message, Request, Response, Result, CloseCode, Handshake};
impl Handler for Client {
fn on_open(&mut self, hs: Handshake) -> Result<()> {
self.handle_on_open(hs)
}
fn on_message(&mut self, msg: Message) -> Result<()> {
self.handle_on_message(msg)
}
fn on_close(&mut self, code: CloseCode, reason: &str) {
self.handle_on_close(code, reason)
}
fn on_request(&mut self, req: &Request) -> Result<(Response)> {
self.handle_on_request(req)
}
}
sockets/on_open.rs
use crate::models::client::Client;
use crate::CLIENTS;
use crate::models::{truck::Truck};
use ws::{Result, Handshake};
impl Client {
pub fn handle_on_open(&mut self, _: Handshake) -> Result<()> {
println!("socket is opened");
Ok(())
}
}

Lifetime inference problem when implementing iterator with refs

I'm implementing a simple Iterator for a struct that contains a ref:
extern crate zip;
extern crate quick_xml;
extern crate chrono;
use std::io::{Seek, Write, Read, Error};
use std::fs::File;
use xlsx_read::zip::read::ZipFile;
use xlsx_read::zip::result::ZipResult;
use xlsx_read::zip::ZipArchive;
use xlsx_read::zip::write::{FileOptions, ZipWriter};
use xlsx_read::quick_xml::Reader as XmlReader;
use xlsx_read::quick_xml::events::Event;
use std::io::BufReader;
use xlsx_read::chrono::prelude::*;
pub struct XlsxFile<'a> {
path: &'a str,
archive: ZipArchive<File>,
sheet_count: usize,
curr: usize,
}
impl<'a> XlsxFile<'a> {
pub fn from(path: &'a str) -> Result<XlsxFile, Error> {
let file = File::open(path)?;
let archive = ZipArchive::new(file)?;
let sheet_count = archive.len();
Ok(XlsxFile {
path: path,
archive: archive,
sheet_count,
curr: 0,
})
}
}
pub struct XlsxSheet<'a> {
pub name: &'a str,
pub index: usize,
}
impl<'a> Iterator for XlsxFile<'a> {
type Item = XlsxSheet<'a>;
fn next(&mut self) -> Option<XlsxSheet<'a>> {
loop {
if self.sheet_count > 0 &&
self.sheet_count > self.curr {
let zip_file = self.archive.by_index(self.curr).unwrap();
let file_name = zip_file.name();
if file_name.contains("xl/worksheets/sheet") {
let sheet = XlsxSheet {
name: file_name, // works fine if String::from(file_name) is used
index: self.curr,
};
self.curr += 1;
return Some(sheet);
}
self.curr += 1;
continue;
} else {
break;
}
}
return None;
}
}
static XLSX_FILE: &'static str = "<location_to_xlsx_file>";
fn main() {
let mut file = xlsx_read::XlsxFile::from(XLSX_FILE).unwrap();
file.for_each(|s| println!("idx: {:?}", s.name));
}
But I get the following error:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/xlsx_read.rs:50:45
|
50 | let zip_file = self.archive.by_index(self.curr).unwrap();
| ^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 46:5...
--> src/xlsx_read.rs:46:5
|
46 | / fn next(&mut self) -> Option<XlsxSheet<'a>> {
47 | | loop {
48 | | if self.sheet_count > 0 &&
49 | | self.sheet_count > self.curr {
... |
66 | | return None;
67 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/xlsx_read.rs:50:32
|
50 | let zip_file = self.archive.by_index(self.curr).unwrap();
| ^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 43:1...
--> src/xlsx_read.rs:43:1
|
43 | impl<'a> Iterator for XlsxFile<'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...so that the expression is assignable:
expected std::option::Option<xlsx_read::XlsxSheet<'a>>
found std::option::Option<xlsx_read::XlsxSheet<'_>>
error: aborting due to previous error
For more information about this error, try `rustc --explain E0495`.
My question is, how to tell Rust compiler to use appropriate lifetime here? Even though I've defined XlsxSheet<'a> with lifetime modifier and want to tie name to &'a str but somehow this doesn't translate into a valid Rust code.
Easy Solution: This problem can be trivially fixed by using String instead of &'a str.
Explanation:
I don't know the definition of by_index, which seems to be quite crucial to this problem. The following reasoning is pure guess and it's not reliable. It's offered only for reference.
self.archive borrows self (which is valid over the entire scope, let's say the lifetime is named 'me), and has lifetime 'me.
Thus the return value of by_index has lifetime 'me.
Oops, XlsxSheet<'me> is not compatible with XlsxSheet<'a> (which is expected)!
What we want here is XlsxSheet<'me> being a subtype of XlsxSheet<'a>, which in turn implies 'me being a subtype of 'a, if XlsxSheet is covariant. Therefore, you can state them explicitly
fn next(&mut self) -> Option<XlsxSheet<'a>> where Self: 'a
// or
impl<'a> Iterator for XlsxFile<'a> + 'a

Values does not live long enough in constructor and setter in OOP Rust

I have the following code:
//! # Messages
/// Represents a simple text message.
pub struct SimpleMessage<'a> {
pub user: &'a str,
pub content: &'a str,
}
impl<'a> SimpleMessage<'a> {
/// Creates a new SimpleMessage.
fn new_msg(u: &'a str, c: &'a str) -> SimpleMessage<'a> {
SimpleMessage { user: u,
content: &c.to_string(), }
}
/// Sets a User in a Message.
pub fn set_user(&mut self, u: User<'a>){
self.user = &u;
}
}
But $ cargo run returns:
error[E0597]: borrowed value does not live long enough
--> src/messages.rs:34:35
|
34 | content: &c.to_string(), }
| ^^^^^^^^^^^^^ temporary value does not live long enough
35 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 28:1...
|
28 | impl<'a> SimpleMessage<'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0597]: `u` does not live long enough
|
54 | self.user = &u;
| ^ borrowed value does not live long enough
55 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 28:1...
--> src/messages.rs:28:1
|
28 | impl<'a> SimpleMessage<'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
I've tried changing the borrowing format of the variables at the function signature and it's contents with no success, it doesn't seem as a borrowing issue, but I don't really understand it, as the lifetime <'a> defined at pub struct SimpleMessage<'a> clearly designates the longest lifetime to it's components, and the impl<'a> SimpleMessage<'a> uses the same lifetime.
What am I missing?
Similar Question:
“borrowed value does not live long enough” when using the builder pattern
Doesn't really help resolve this issue.
str.to_string() will create an owned String, which will not live longer than the new_msg method, so you will not be able to pass a slice of it anywhere. Instead, just use the &str argument, since it is valid for the lifetime 'a, which is what you need.
/// Creates a new SimpleMessage.
fn new_msg(u: &'a User, c: &'a str) -> SimpleMessage<'a> {
SimpleMessage { user: u, content: c, }
}
The other method also has a problem. You are trying to give an owned User, but the SimpleMessage struct requires a reference. It should look like this:
/// Sets a User in a Message.
pub fn set_user(&mut self, u: &'a User<'a>){
self.user = u;
}
You don't need to_string if you want to store a reference to a string. Also, set_user must also take a reference, not a value (because there's no field in the struct to store it):
pub struct User<'a> {
pub data: &'a u8,
}
/// Represents a simple text message.
pub struct SimpleMessage<'a> {
pub user: &'a User<'a>,
pub content: &'a str,
}
impl<'a> SimpleMessage<'a> {
fn new_msg(user: &'a User, content: &'a str) -> SimpleMessage<'a> {
SimpleMessage { user, content }
}
pub fn set_user(&mut self, user: &'a User<'a>) {
self.user = user;
}
}
fn main() {
let data1 = 1;
let data2 = 2;
let user1 = User { data: &data1 };
let user2 = User { data: &data2 };
let mut msg = SimpleMessage::new_msg(&user1, "test");
msg.set_user(&user2);
}
Playground
If you want to save strings that are created at runtime (for example, with format!() call) you may want to store a String:
pub struct SimpleMessage<'a> {
pub user: &'a User<'a>,
pub content: String,
}
. . .
fn new_msg(user: &'a User, content: String) -> SimpleMessage<'a> {
SimpleMessage { user, content }
}
. . .
let mut msg = SimpleMessage::new_msg(&user1, format!("created at {}", "runtime"));
Playground