How do I transform io::Result into anyhow::Result? - error-handling

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))
})
})
}

Related

Expected struct Config, found () while using process::exit

I'm new to Rust and going through the official book. I'm working on a simple grep example and want to make an exit function which I can use in different places. Unfortunately using this function in a closure in unwrap_or_else causes a compile error. This not clear to me why, because when I use the contents of the function directly in the closure it works.
Here is my main.rs file:
use std::env;
use std::fs;
use std::process;
use std::error::Error;
use std::fmt::Display;
struct Config{
query: String,
filename: String,
}
impl Config {
fn new(input: &[String]) -> Result<Config, &'static str> {
if input.len() < 3 {
return Err("Not enough arguments provided.");
}
let query = input[1].clone();
let filename = input[2].clone();
Ok(Config { query, filename })
}
}
fn run(cfg: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(&cfg.filename)?;
contents.find(&cfg.query).expect("Corrupted text file.");
Ok(())
}
fn exit<T: Display>(msg: &str, err: T) {
println!("{}: {}", msg, err);
process::exit(1);
}
fn main() {
let args: Vec<String> = env::args().collect();
println!("{:?}", args);
let cfg = Config::new(&args).unwrap_or_else(|err| {
exit("Problem parsing arguments", err);
});
if let Err(err) = run(cfg) {
exit("Application error", err);
}
}
And here is the compile error:
error[E0308]: mismatched types
--> src\main.rs:41:55
|
41 | let cfg = Config::new(&args).unwrap_or_else(|err| {
| _______________________________________________________^
42 | | exit("Problem parsing arguments", err);
43 | | });
| |_____^ expected struct `Config`, found `()`
When I change the Config::new(&args).unwrap_or_else closure to this, it works:
let cfg = Config::new(&args).unwrap_or_else(|err| {
println!("Problem parsing arguments: {}", err);
process::exit(1);
});
I got stuck on this too. You need to import the process library:
use std::process;
edit: On second look you did import it. For others who run into this problem then that was mine. I got the same error.
You need to specify, that your exit() function never returns, i.e. add -> !.
These functions are called "diverging functions".
fn exit<T: Display>(msg: &str, err: T) -> ! {
println!("{}: {}", msg, err);
process::exit(1);
}
However, you should be careful with using process::exit(). Because it will terminate the current process, and not invoke destructors.
To ensure destructors are handled, you should instead do something like this:
fn main() {
std::process::exit(match run() {
Ok(_) => 0,
Err(code) => code,
});
}
fn run() -> Result<(), i32> {
// Application logic here, i.e. what you'd otherwise have had in `main()`
Ok(())
}
The example is a minor adapted version of the one found at the documentation for process::exit().
To add to vallentin's answer here's the more idiomatic version which doesn't use process::exit:
use std::env;
use std::error::Error;
use std::fmt::Display;
use std::fs;
use std::process;
struct Config {
query: String,
filename: String,
}
impl Config {
fn new(input: &[String]) -> Result<Config, &'static str> {
if input.len() < 3 {
return Err("Not enough arguments provided.");
}
let query = input[1].clone();
let filename = input[2].clone();
Ok(Config { query, filename })
}
}
fn run(cfg: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(&cfg.filename)?;
// convert Option to a Result so we can use `?`
contents.find(&cfg.query).ok_or("Corrupted text file.")?;
Ok(())
}
// you can return a Result from main and Rust will
// print the error to the user if there is one
fn main() -> Result<(), Box<dyn Error>> {
let args: Vec<String> = env::args().collect();
println!("{:?}", args);
// use `?` instead of `exit` function
let cfg = Config::new(&args)?;
run(cfg)?;
Ok(())
}
playground

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

How can we write a generic function for checking Serde serialization and deserialization?

In a project where custom Serde (1.0) serialization and deserialization methods are involved, I have relied on this test routine to check whether serializing an object and back would yield an equivalent object.
// let o: T = ...;
let buf: Vec<u8> = to_vec(&o).unwrap();
let o2: T = from_slice(&buf).unwrap();
assert_eq!(o, o2);
Doing this inline works pretty well. My next step towards reusability was to make a function check_serde for this purpose.
pub fn check_serde<T>(o: T)
where
T: Debug + PartialEq<T> + Serialize + DeserializeOwned,
{
let buf: Vec<u8> = to_vec(&o).unwrap();
let o2: T = from_slice(&buf).unwrap();
assert_eq!(o, o2);
}
This works well for owning types, but not for types with lifetime bounds (Playground):
check_serde(5);
check_serde(vec![1, 2, 5]);
check_serde("five".to_string());
check_serde("wait"); // [E0279]
The error:
error[E0279]: the requirement `for<'de> 'de : ` is not satisfied (`expected bound lifetime parameter 'de, found concrete lifetime`)
--> src/main.rs:24:5
|
24 | check_serde("wait"); // [E0277]
| ^^^^^^^^^^^
|
= note: required because of the requirements on the impl of `for<'de> serde::Deserialize<'de>` for `&str`
= note: required because of the requirements on the impl of `serde::de::DeserializeOwned` for `&str`
= note: required by `check_serde`
As I wish to make the function work with these cases (including structs with string slices), I attempted a new version with an explicit object deserialization lifetime:
pub fn check_serde<'a, T>(o: &'a T)
where
T: Debug + PartialEq<T> + Serialize + Deserialize<'a>,
{
let buf: Vec<u8> = to_vec(o).unwrap();
let o2: T = from_slice(&buf).unwrap();
assert_eq!(o, &o2);
}
check_serde(&5);
check_serde(&vec![1, 2, 5]);
check_serde(&"five".to_string());
check_serde(&"wait"); // [E0405]
This implementation leads to another issue, and it won't compile (Playground).
error[E0597]: `buf` does not live long enough
--> src/main.rs:14:29
|
14 | let o2: T = from_slice(&buf).unwrap();
| ^^^ does not live long enough
15 | assert_eq!(o, &o2);
16 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 10:1...
--> src/main.rs:10:1
|
10 | / pub fn check_serde<'a, T>(o: &'a T)
11 | | where T: Debug + PartialEq<T> + Serialize + Deserialize<'a>
12 | | {
13 | | let buf: Vec<u8> = to_vec(o).unwrap();
14 | | let o2: T = from_slice(&buf).unwrap();
15 | | assert_eq!(o, &o2);
16 | | }
| |_^
I have already expected this one: this version implies that the serialized content (and so the deserialized object) lives as long as the input object, which is not true. The buffer is only meant to live as long as the function's scope.
My third attempt seeks to build owned versions of the original input, thus evading the issue of having a deserialized object with different lifetime boundaries. The ToOwned trait appears to suit this use case.
pub fn check_serde<'a, T: ?Sized>(o: &'a T)
where
T: Debug + ToOwned + PartialEq<<T as ToOwned>::Owned> + Serialize,
<T as ToOwned>::Owned: Debug + DeserializeOwned,
{
let buf: Vec<u8> = to_vec(&o).unwrap();
let o2: T::Owned = from_slice(&buf).unwrap();
assert_eq!(o, &o2);
}
This makes the function work for plain string slices now, but not for composite objects containing them (Playground):
check_serde(&5);
check_serde(&vec![1, 2, 5]);
check_serde(&"five".to_string());
check_serde("wait");
check_serde(&("There's more!", 36)); // [E0279]
Again, we stumble upon the same error kind as the first version:
error[E0279]: the requirement `for<'de> 'de : ` is not satisfied (`expected bound lifetime parameter 'de, found concrete lifetime`)
--> src/main.rs:25:5
|
25 | check_serde(&("There's more!", 36)); // [E0279]
| ^^^^^^^^^^^
|
= note: required because of the requirements on the impl of `for<'de> serde::Deserialize<'de>` for `&str`
= note: required because of the requirements on the impl of `for<'de> serde::Deserialize<'de>` for `(&str, {integer})`
= note: required because of the requirements on the impl of `serde::de::DeserializeOwned` for `(&str, {integer})`
= note: required by `check_serde`
Granted, I'm at a loss. How can we build a generic function that, using Serde, serializes an object and deserializes it back into a new object? In particular, can this function be made in Rust (stable or nightly), and if so, what adjustments to my implementation are missing?
Unfortunately, what you need is a feature that is not yet implemented in Rust: generic associated types.
Let's look at a different variant of check_serde:
pub fn check_serde<T>(o: T)
where
for<'a> T: Debug + PartialEq<T> + Serialize + Deserialize<'a>,
{
let buf: Vec<u8> = to_vec(&o).unwrap();
let o2: T = from_slice(&buf).unwrap();
assert_eq!(o, o2);
}
fn main() {
check_serde("wait"); // [E0279]
}
The problem here is that o2 cannot be of type T: o2 refers to buf, which is a local variable, but type parameters cannot be inferred to types constrained by a lifetime that is restricted to the function's body. We'd like for T to be something like &str without a specific lifetime attached to it.
With generic associated types, this could be solved with something like this (obviously I can't test it, since it's not implemented yet):
trait SerdeFamily {
type Member<'a>: Debug + for<'b> PartialEq<Self::Member<'b>> + Serialize + Deserialize<'a>;
}
struct I32Family;
struct StrFamily;
impl SerdeFamily for I32Family {
type Member<'a> = i32; // ignoring a parameter is allowed
}
impl SerdeFamily for StrFamily {
type Member<'a> = &'a str;
}
pub fn check_serde<'a, Family>(o: Family::Member<'a>)
where
Family: SerdeFamily,
{
let buf: Vec<u8> = to_vec(&o).unwrap();
// `o2` is of type `Family::Member<'b>`
// with a lifetime 'b different from 'a
let o2: Family::Member = from_slice(&buf).unwrap();
assert_eq!(o, o2);
}
fn main() {
check_serde::<I32Family>(5);
check_serde::<StrFamily>("wait");
}
The answer from Francis Gagné has shown that we cannot do this efficiently without generic associated types. Establishing deep ownership of the deserialized object is a possible work-around which I describe here.
The third attempt is very close to a flexible solution, but it falls short due to how std::borrow::ToOwned works. The trait is not suitable for retrieving a deeply owned version of an object. Attempting to use the implementation of ToOwned for &str, for instance, gives you another string slice.
let a: &str = "hello";
let b: String = (&a).to_owned(); // expected String, got &str
Likewise, the Owned type for a struct containing string slices cannot be a struct containing Strings. In code:
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Foo<'a>(&str, i32);
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct FooOwned(String, i32);
We cannot impl ToOwned for Foo to provide FooOwned because:
If we derive Clone, the implementation of ToOwned for T: Clone is only applicable to Owned = Self.
Even with a custom implementation of ToOwned, the trait requires that the owned type can be borrowed into the original type (due to the constraint Owned: Borrow<Self>). That is, we are supposed to be able to retrieve a &Foo(&str, i32) out of a FooOwned, but their internal structure is different, and so this is not attainable.
This means that, in order to follow the third approach, we need a different trait. Let's have a new trait ToDeeplyOwned which turns an object into a fully owned one, with no slices or references involved.
pub trait ToDeeplyOwned {
type Owned;
fn to_deeply_owned(&self) -> Self::Owned;
}
The intent here is to produce a deep copy out of anything. There doesn't seem to be an easy catch-all implementation, but some tricks are possible. First, we can implement it to all reference types where T: ToDeeplyOwned.
impl<'a, T: ?Sized + ToDeeplyOwned> ToDeeplyOwned for &'a T {
type Owned = T::Owned;
fn to_deeply_owned(&self) -> Self::Owned {
(**self).to_deeply_owned()
}
}
At this point we would have to selectively implement it to non-reference types where we know it's ok. I wrote a macro for making this process less verbose, which uses to_owned() internally.
macro_rules! impl_deeply_owned {
($t: ty, $t2: ty) => { // turn $t into $t2
impl ToDeeplyOwned for $t {
type Owned = $t2;
fn to_deeply_owned(&self) -> Self::Owned {
self.to_owned()
}
}
};
($t: ty) => { // turn $t into itself, self-contained type
impl ToDeeplyOwned for $t {
type Owned = $t;
fn to_deeply_owned(&self) -> Self::Owned {
self.to_owned()
}
}
};
}
For the examples in the question to work, we need at least these:
impl_deeply_owned!(i32);
impl_deeply_owned!(String);
impl_deeply_owned!(Vec<i32>);
impl_deeply_owned!(str, String);
Once we implement the necessary traits on Foo/FooOwned and adapt serde_check to use the new trait, the code now compiles and runs successfully (Playground):
#[derive(Debug, PartialEq, Serialize)]
struct Foo<'a>(&'a str, i32);
#[derive(Debug, PartialEq, Clone, Deserialize)]
struct FooOwned(String, i32);
impl<'a> ToDeeplyOwned for Foo<'a> {
type Owned = FooOwned;
fn to_deeply_owned(&self) -> FooOwned {
FooOwned(self.0.to_string(), self.1)
}
}
impl<'a> PartialEq<FooOwned> for Foo<'a> {
fn eq(&self, o: &FooOwned) -> bool {
self.0 == o.0 && self.1 == o.1
}
}
pub fn check_serde<'a, T: ?Sized>(o: &'a T)
where
T: Debug + ToDeeplyOwned + PartialEq<<T as ToDeeplyOwned>::Owned> + Serialize,
<T as ToDeeplyOwned>::Owned: Debug + DeserializeOwned,
{
let buf: Vec<u8> = to_vec(&o).unwrap();
let o2: T::Owned = from_slice(&buf).unwrap();
assert_eq!(o, &o2);
}
// all of these are ok
check_serde(&5);
check_serde(&vec![1, 2, 5]);
check_serde(&"five".to_string());
check_serde("wait");
check_serde(&"wait");
check_serde(&Foo("There's more!", 36));
Update (04.09.2021):
The latest nightly has some fixes around GATs which basically allows the original example:
#![feature(generic_associated_types)]
use serde::{Deserialize, Serialize};
use serde_json::{from_slice, to_vec};
use std::fmt::Debug;
trait SerdeFamily {
type Member<'a>:
Debug +
for<'b> PartialEq<Self::Member<'b>> +
Serialize +
Deserialize<'a>;
}
struct I32Family;
struct StrFamily;
impl SerdeFamily for I32Family {
type Member<'a> = i32;
}
impl SerdeFamily for StrFamily {
type Member<'a> = &'a str;
}
fn check_serde<F: SerdeFamily>(o: F::Member<'_>) {
let buf: Vec<u8> = to_vec(&o).unwrap();
let o2: F::Member<'_> = from_slice(&buf).unwrap();
assert_eq!(o, o2);
}
fn main() {
check_serde::<I32Family>(5);
check_serde::<StrFamily>("wait");
}
The example above compiles now: playground.
As of now it's possible to implement this on rust nightly (with an explicit variance workaround):
#![feature(generic_associated_types)]
use serde::{Deserialize, Serialize};
use serde_json::{from_slice, to_vec};
use std::fmt::Debug;
trait SerdeFamily {
type Member<'a>: Debug + PartialEq + Serialize + Deserialize<'a>;
// https://internals.rust-lang.org/t/variance-of-lifetime-arguments-in-gats/14769/19
fn upcast_gat<'short, 'long: 'short>(long: Self::Member<'long>) -> Self::Member<'short>;
}
struct I32Family;
struct StrFamily;
impl SerdeFamily for I32Family {
type Member<'a> = i32; // we can ignore parameters
fn upcast_gat<'short, 'long: 'short>(long: Self::Member<'long>) -> Self::Member<'short> {
long
}
}
impl SerdeFamily for StrFamily {
type Member<'a> = &'a str;
fn upcast_gat<'short, 'long: 'short>(long: Self::Member<'long>) -> Self::Member<'short> {
long
}
}
fn check_serde<F: SerdeFamily>(o: F::Member<'_>) {
let buf: Vec<u8> = to_vec(&o).unwrap();
let o2: F::Member<'_> = from_slice(&buf).unwrap();
assert_eq!(F::upcast_gat(o), o2);
}
fn main() {
check_serde::<I32Family>(5);
check_serde::<StrFamily>("wait");
}
Playground
Simple (but a little awkward) solution: Provide buf from outside of the function.
pub fn check_serde<'a, T>(o: &'a T, buf: &'a mut Vec<u8>)
where
T: Debug + PartialEq<T> + Serialize + Deserialize<'a>,
{
*buf = to_vec(o).unwrap();
let o2: T = from_slice(buf).unwrap();
assert_eq!(o, &o2);
}
buf can be reused with Cursor
pub fn check_serde_with_cursor<'a, T>(o: &'a T, buf: &'a mut Vec<u8>)
where
T: Debug + PartialEq<T> + Serialize + Deserialize<'a>,
{
buf.clear();
let mut cursor = Cursor::new(buf);
to_writer(&mut cursor, o).unwrap();
let o2: T = from_slice(cursor.into_inner()).unwrap();
assert_eq!(o, &o2);
}

Composing errors in Rust : concating results on Ok

I have following code. It works.
But i am more interested in writing make_tea where i call two functions : get_milk_from_cow and pour_milk. They both return Result<String, TeaError>.
How can i compose them so that i can keep concating Strings if they succeed otherwise return error.
enum TeaError {
NoMilk,
NoCup,
NoCow,
NoGas,
NoSomething,
}
fn get_milk_from_cow(cow: bool) -> Result<String, TeaError> {
if cow {
Ok(String::from("get milk from cow"))
} else {
Err(TeaError::NoCow)
}
}
fn pour_milk(milk: bool) -> Result<String, TeaError> {
if milk {
Ok(String::from("poured milk"))
} else {
Err(TeaError::NoMilk)
}
}
fn make_tea() -> Result<String, TeaError> {
let mut process = String::new();
let step_cow = get_milk_from_cow(true)?;
let step_milk = pour_milk(true)?;
process.push_str(step_cow.as_str());
process.push_str(step_milk.as_str());
Ok(process)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn attemp_tea_one() {
match pour_milk(false) {
Err(v) => match v {
TeaError::NoMilk => assert!(true),
_ => assert!(false)
},
Ok(_) => assert!(false)
};
}
#[test]
fn attemp_tea_two() {
match make_tea() {
Err(_) => assert!(false),
Ok(_) => assert!(true)
};
}
}
I tried :
process.push_str(get_milk_from_cow(true)?
.push_str(pour_milk(true)?.as_str()))
but it gives :
error[E0308]: mismatched types
--> src/errors.rs:27:22
|
27 | process.push_str(get_milk_from_cow(true)?.push_str(pour_milk(true)?.as_str()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &str, found ()
|
= note: expected type `&str`
found type `()`
as push_str does not return string.
Edit:
fn append(s1: String, s2: String) -> String {
s1 + s2.as_str()
}
fn make_tea() -> Result<String, TeaError> {
let process = append(get_milk_from_cow(true)?,
pour_milk(true)?);
Ok(process)
}
so, i can do append(append(funcA(), funcB()), funcC()) and so on..
I am still learning about lifetimes and think about whether append can still be improved in memory allocation.
This code does redundant work on the starred lines:
fn make_tea() -> Result<String, TeaError> {
* let mut process = String::new();
let step_cow = get_milk_from_cow(true)?;
let step_milk = pour_milk(true)?;
* process.push_str(step_cow.as_str());
process.push_str(step_milk.as_str());
Ok(process)
}
process.push_str(step_cow.as_str()) just makes an unneeded copy of step_cow. Instead, try
fn make_tea() -> Result<String, TeaError> {
let mut process = get_milk_from_cow(true)?;
process.push_str(&pour_milk(true)?);
Ok(process)
}
or, more conveniently,
fn make_tea() -> Result<String, TeaError> {
Ok(get_milk_from_cow(true)? + &pour_milk(true)?)
}
First things first: there appears to be nothing wrong with your code above, but I'll be assuming that you are looking for something more idiomatic.
Although requiring slightly more memory than your approach, the most elegant way to concatenate results of strings is this:
fn make_tea() -> Result<String, TeaError> {
vec![get_milk_from_cow(true), pour_milk(true)].into_iter()
.collect()
}
Playground
Explanation:
The vector is consumed into an iterator of Result<String, TeaError> (owned, not references).
collect then relies on two implementations of FromIterator:
impl<A, E, V> FromIterator<Result<A, E>> for Result<V, E>
where V: FromIterator<A> produces a result containing either the result of building A from an iterator of T or the first error retrieved from the iterator. This is like turning an iterator of results into a result with a collected iterator.
impl FromIterator<String> for String concatenates all strings into an owned string
So, as soon as you already have an iterator that turns your process into a sequence of independent operations, you can just collect them.
Now, in order to prevent subsequent operations from being called after an error is found, then it's easier to stick to the ? operator.
fn make_tea() -> Result<String, TeaError> {
Ok(vec![get_milk_from_cow(true)?, pour_milk(true)?].into_iter()
.collect())
}
Playground
The vector had to be created because arrays do not provide iterators of owned elements (&T instead of T). However, we can go around that with an extra mapping:
fn make_tea() -> Result<String, TeaError> {
Ok([get_milk_from_cow(true)?, pour_milk(true)?].into_iter()
.map(|a| a.as_str())
.collect())
}
This will map elements from &String into &str, which can be collected likewise.

How can I automatically implement FromIterator?

I have written a trait that specifies some methods similar to those of Vec:
pub trait Buffer {
type Item;
fn with_capacity(c: usize) -> Self;
fn push(&mut self, item: Self::Item);
}
I would like to implement FromIterator for all types that implement Buffer, as follows:
impl<T> iter::FromIterator<T::Item> for T
where T: Buffer
{
fn from_iter<I>(iter: I) -> Self
where I: IntoIterator<Item = T>
{
let mut iter = iter.into_iter();
let (lower, _) = iter.size_hint();
let ans = Self::with_capacity(lower);
while let Some(x) = iter.next() {
ans.push(x);
}
ans
}
}
The compiler won't let me:
error[E0210]: type parameter `T` must be used as the type parameter
for some local type (e.g. `MyStruct<T>`); only traits defined in the
current crate can be implemented for a type parameter
I think I understand the error message; it is preventing me from writing code that is incompatible with possible future changes to the standard library.
The only way around this error appears to be to implement FromIterator separately for every type for which I implement Buffer. This will involve copying out exactly the same code many times. Is there a a way to share the same implementation between all Buffer types?
You can't implement a trait from another crate for an arbitrary type, only for a type from your crate. However, you can move the implementation to a function and reduce amount of duplicated code:
fn buffer_from_iter<I, B>(iter: I) -> B
where I: IntoIterator<Item = B::Item>,
B: Buffer
{
let mut iter = iter.into_iter();
let (lower, _) = iter.size_hint();
let mut ans = B::with_capacity(lower);
while let Some(x) = iter.next() {
ans.push(x);
}
ans
}
struct S1;
impl Buffer for S1 {
type Item = i32;
fn with_capacity(c: usize) -> Self { unimplemented!() }
fn push(&mut self, item: Self::Item) { unimplemented!() }
}
impl std::iter::FromIterator<<S1 as Buffer>::Item> for S1 {
fn from_iter<I>(iter: I) -> Self
where I: IntoIterator<Item = <S1 as Buffer>::Item>
{
buffer_from_iter(iter)
}
}
This implementation of FromIterator can be wrapped into a macro to further reduce code duplication.