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.
Related
We can use or_else and ? to return Err early if we encounter None:
let o = None;
let x = o.ok_or(666)?;
But what if we expect the opposite? Return early if something is Some:
let o = Some(42);
if o.is_some() {
return Err(666);
}
Can we somehow do this with ? as well?
I assume this is all about having it be a single line and saving characters.
You could use map_or(), to convert Some(_) into Err(666) and None into Ok(()). However, this isn't idiomatic, I'd also personally stick to if is_some() { return Err(666); }, as what that is doing is more clear to the reader.
fn foo(o: Option<i32>) -> Result<(), i32> {
o.map_or(Ok(()), |_| Err(666))?;
Ok(())
}
fn main() {
println!("{:?}", foo(None));
println!("{:?}", foo(Some(42)));
}
Outputs:
Ok(())
Err(666)
You could also create your own ErrOnSome trait. Naming the method e.g. err_on_some() it would be more clear and assumable to the reader what's going on, even without knowing the implementation of the err_on_some() method.
trait ErrOnSome {
fn err_on_some<F, E>(&self, f: F) -> Result<(), E>
where
F: FnOnce() -> Result<(), E>;
}
impl<T> ErrOnSome for Option<T> {
fn err_on_some<F, E>(&self, f: F) -> Result<(), E>
where
F: FnOnce() -> Result<(), E>,
{
match self {
None => Ok(()),
Some(_) => f(),
}
}
}
fn foo(o: Option<i32>) -> Result<(), i32> {
o.err_on_some(|| Err(666))?;
Ok(())
}
Using the same main() it produces the same output of course.
Edit: Old answer - I misread and thought it was about returning Option
If the contained value is a primitive, i.e. cheap to create, then you could stick to xor(). However, this isn't idiomatic, I'd also personally stick to if is_some() { return Err(666); }, as what that is doing is more clear to the reader.
fn foo(o: Option<i32>) -> Option<()> {
o.xor(Some(666))?;
Some(())
}
fn main() {
println!("{:?}", foo(None));
println!("{:?}", foo(Some(42)));
}
Outputs:
Some(123)
None
Can we somehow [return Err on Some] with ? as well?
I wouldn't call it idiomatic, but you can use Option::map to map the Some variant of the option into an Err. This will transform Option<T> to Option<Result<X, E>>, so you need to transpose it into a Result<Option<X>, E> and use ? on that:
fn x() -> Result<(), i32> {
let o = Some(42);
o.map(|_| Err::<(), _>(666)).transpose()?;
Ok(())
}
The turbofish on Err provides a necessary type hint because otherwise Rust can't determine the X of Result<X, E> mentioned above, and a result must have both an ok and an error type. I've chosen (), but it's completely unrelated to the () in the return type of the function; it could have been any other type because the ? operator only cares about the error type of the Result.
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
I'm trying to implement a simple interpreter in Rust, for which I have created a Tokens struct, which takes source characters and produces either a Token or a ScanError inside a Result:
pub struct Tokens<'src> {
chars: Chars<'src>,
}
impl<'src> Iterator for Tokens<'src> {
type Item = Result<Token, ScanError>;
fn next(&mut self) -> Option<Result<Token, ScanError>> {
// ...
}
}
Since Result implements FromIterator, it is simple to collect the result to either the first ScanError or a vector of Tokens:
fn scan_tokens(source: &str) -> Result<Vec<Token>, ScanError> {
let iter = Tokens {
chars: source.chars(),
};
iter.collect()
}
In the case of multiple errors I really want to return every error:
fn scan_tokens(source: &str) -> Result<Vec<Token>, Vec<ScanError>> {
// what goes here?
}
It isn't possible as far as I know to implement my own version of FromIterator because neither that trait or Result are local to my crate. Can anyone suggest a clean way of doing this?
I have written an implementation using partition on the iterator, then unwrapping each Result, below, but it's not fun to read and doesn't feel like good use of iterators:
type T = Vec<Result<Token, ScanError>>;
fn scan_tokens(source: &str) -> Result<Vec<Token>, Vec<ScanError>> {
let iter = Tokens {
chars: source.chars(),
};
let (tokens_results, error_results): (T, T) = iter.partition(|result| result.is_ok());
let errors: Vec<ScanError> = error_results
.into_iter()
.map(|result| result.unwrap_err())
.collect();
if errors.len() > 0 {
return Err(errors);
}
Ok(tokens_results
.into_iter()
.map(|result| result.unwrap())
.collect())
}
unwrapping each Result
I would use itertools' partition_map to avoid the need to unwrap:
use itertools::{Either, Itertools}; // 0.8.0
fn iterator() -> impl Iterator<Item = Result<i32, bool>> {
vec![Ok(1), Err(false), Ok(2), Err(true), Ok(3)].into_iter()
}
fn example() -> Result<Vec<i32>, Vec<bool>> {
let (values, errors): (Vec<_>, Vec<_>) = iterator().partition_map(|v| match v {
Ok(v) => Either::Left(v),
Err(e) => Either::Right(e),
});
if errors.is_empty() {
Ok(values)
} else {
Err(errors)
}
}
See also:
What's the most idiomatic way of working with an Iterator of Results?
How do I stop iteration and return an error when Iterator::map returns a Result::Err?
How do I perform iterator computations over iterators of Results without collecting to a temporary vector?
You could also use the fact that Option and Result implement IntoIterator to avoid the exact unwrap, although this still processes one collection twice:
fn example2() -> Result<Vec<i32>, Vec<bool>> {
let (values, errors): (Vec<_>, Vec<_>) = iterator().partition(|result| result.is_ok());
if errors.is_empty() {
Ok(values.into_iter().flat_map(Result::ok).collect())
} else {
Err(errors.into_iter().flat_map(Result::err).collect())
}
}
See also:
Why does `Option` support `IntoIterator`?
An imperative solution is often the most expressive and efficient way to implement some algorithm. It's Rust, not Haskell; not everything needs to be functional.
fn scan_tokens(source: &str) -> Result<Vec<Token>, Vec<ScanError>> {
let iter = Tokens {
chars: source.chars(),
};
let mut tokens = Vec::new();
let mut errors = Vec::new();
for result in iter {
match result {
Ok(token) => {
tokens.push(token);
}
Err(e) => {
errors.push(e);
}
}
}
if errors.is_empty() {
Ok(tokens)
} else {
Err(errors)
}
}
I would like to refactor this Rust code for calculating the largest series product and make it as efficient and elegant as possible. I feel that
lsp(string_digits: &str, span: usize) -> Result<u64, Error>
could be done in a way to make it much more elegant than it is right now. Could lsp be implemented with only one series of chained iterator methods?
#[derive(Debug, PartialEq)]
pub enum Error {
SpanTooLong,
InvalidDigit(char),
}
fn sp(w: &[u8]) -> u64 {
w.iter().fold(1u64, |acc, &d| acc * u64::from(d))
}
pub fn lsp(string_digits: &str, span: usize) -> Result<u64, Error> {
let invalid_chars = string_digits
.chars()
.filter(|ch| !ch.is_numeric())
.collect::<Vec<_>>();
if span > string_digits.len() {
return Err(Error::SpanTooLong);
} else if !invalid_chars.is_empty() {
return Err(Error::InvalidDigit(invalid_chars[0]));
} else if span == 0 || string_digits.is_empty() {
return Ok(1);
}
let vec_of_u8_digits = string_digits
.chars()
.map(|ch| ch.to_digit(10).unwrap() as u8)
.collect::<Vec<_>>();
let lsp = vec_of_u8_digits
.windows(span)
.max_by(|&w1, &w2| sp(w1).cmp(&sp(w2)))
.unwrap();
Ok(sp(lsp))
}
Not sure if this is the most elegant way, but I've given it a try, hope the new version is equivalent to the given program.
Two things will be needed in this case: First, we need a data structure that provides the sliding window "on the fly" and second a function that ends the iteration early if the conversion yields an error.
For the former I've chosen a VecDeque since span is dynamic. For the latter there is a function called process_results in the itertools crate. It converts an iterator over results to an iterator over the unwrapped type and stops iteration if an error is encountered.
I've also slightly changed the signature of sp to accept any iterator over u8.
This is the code:
use std::collections::VecDeque;
use itertools::process_results;
#[derive(Debug, PartialEq)]
pub enum Error {
SpanTooLong,
InvalidDigit(char),
}
fn sp(w: impl Iterator<Item=u8>) -> u64 {
w.fold(1u64, |acc, d| acc * u64::from(d))
}
pub fn lsp(string_digits: &str, span: usize) -> Result<u64, Error> {
if span > string_digits.len() {
return Err(Error::SpanTooLong);
} else if span == 0 || string_digits.is_empty() {
return Ok(1);
}
let mut init_state = VecDeque::new();
init_state.resize(span, 0);
process_results(string_digits.chars()
.map(|ch| ch.to_digit(10)
.map(|d| d as u8)
.ok_or(Error::InvalidDigit(ch))),
|digits|
digits.scan(init_state, |state, digit| {
state.pop_back();
state.push_front(digit);
Some(sp(state.iter().cloned()))
})
.max()
.unwrap()
)
}
I want to implement the IntoIterator trait for a struct containing a String. The iterator is based on the chars() iterator, is supposed to count the '1' chars and accumulate the result. This is a simplified version of what I got so far:
use std::iter::Map;
use std::str::Chars;
fn main() {
let str_struct = StringStruct { system_string: String::from("1101") };
for a in str_struct {
println!("{}", a);
}
}
struct StringStruct {
system_string: String
}
impl IntoIterator for StringStruct {
type Item = u32;
type IntoIter = Map<Chars, Fn(char) -> u32>;
fn into_iter(self) -> Self::IntoIter {
let count = 0;
return self.system_string.chars().map(|c| match c {
Some('1') => {
count += 1;
return Some(count);
},
Some(chr) => return Some(count),
None => return None
});
}
}
Expected output: 1, 2, 2, 3
This fails with:
error[E0107]: wrong number of lifetime parameters: expected 1, found 0
--> src/main.rs:17:25
|
17 | type IntoIter = Map<Chars, Fn(char) -> u32>;
| ^^^^^ expected 1 lifetime parameter
The chars iterator should have the same lifetime as the StringStruct::system_string, but I have no idea how to express this or if this approach is viable at all.
To answer the question you asked, I'd recommend to impl IntoIterator for &StringStruct (a reference to a StringStruct instead of the struct directly). The code would look like this:
impl<'a> IntoIterator for &'a StringStruct {
type Item = u32;
type IntoIter = Map<Chars<'a>, Fn(char) -> u32>;
// ...
}
However, you will notice many more errors that have a different origin afterwards. The next error that pops up is that Fn(char) -> u32 does not have a constant size at compile time.
The problem is that you try to name the type of your closure by writing Fn(char) -> u32. But this is not the type of your closure, but merely a trait which is implemented by the closure. The type of a closure can't be named (sometimes called "Voldemort type").
This means that, right now, you can't specify the type of a Map<_, _> object. This is a known issue; the recently accepted impl Trait-RFC might offer a workaround for cases like this. But right now, it's not possible, sorry.
So how to solve it then? You need to create your own type that implements Iterator and use it instead of Map<_, _>. Note that you can still use the Chars iterator. Here is the full solution:
struct StringStructIter<'a> {
chars: Chars<'a>,
count: u32,
}
impl<'a> Iterator for StringStructIter<'a> {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
self.chars.next().map(|c| {
if c == '1' {
self.count += 1;
}
self.count
})
}
}
impl<'a> IntoIterator for &'a StringStruct {
type Item = u32;
type IntoIter = StringStructIter<'a>;
fn into_iter(self) -> Self::IntoIter {
StringStructIter {
chars: self.system_string.chars(),
count: 0,
}
}
}
fn main() {
let str_struct = StringStruct { system_string: String::from("1101") };
for a in &str_struct {
println!("{}", a);
}
}
And just a small note: an explicit return when not necessary is considered bad style in Rust. Better stick to rule and write idiomatic code by removing return whenever possible ;-)