I'm reading through the Rust book and working on part of the minigrep where it asks you to write some unit tests on the Config::new function. I'm failing on something that I didn't expect and don't understand why (Google-fu is failing me as well).
This fails
#[cfg(test)]
mod new_config_tests {
use super::*;
#[test]
fn it_can_create_a_new_config() {
let expected_query = "expected_qury";
let expected_filename = "expected_filename.txt";
let args: Vec<String> = vec!["program/path".to_string(), expected_query.to_string(), expected_filename.to_string()];
// failing line
let actual = Config::new(&args).unwrap_or_else(|err| { assert!(false); });
}
}
impl Config {
pub fn new(args: &[String]) -> Result<Config, &'static str> {
if args.len() < 3 {
return Err("not enough arguments\n");
}
let query = args[1].clone();
let filename = args[2].clone();
Ok(Config { query, filename })
}
}
with
error[E0308]: mismatched types
--> src/lib.rs:19:62
|
19 | let actual = Config::new(&args).unwrap_or_else(|err| { assert!(false); });
| ^^^^^^^^^^^^^^^^^^^ expected struct `Config`, found `()`
In this test, I'm just making sure that I can create a new Config and want it to fail if the Config::new function fails. I thought that using assert would be correct so that the test framework would handle the failure. If I change the assert to panic, then the tests pass as expected. Is using panic correct in the above scenario?
The problem is that during type-checking, the compiler doesn't (yet) realize that assert!(false) will always fail, so it has to assume that it may pass, resulting in a value of type () which is incompatible with the expected Config type.
Conversely, if you replace the assert with a call to panic, the compiler knows that panic never returns, so it doesn't matter if there is no Config to return. (Strictly speaking, panic typechecks as returning a value of type !, aka the "never" type, which is compatible with everything).
IMO you should never use assert!(false), and instead use panic when you know that a condition is always fatal. This makes your intent clearer.
Although in this specific case, it would probably be better to assert that the result is Ok:
assert!(Config::new(&args).is_ok());
Related
In the following code I would like to get the 2nd string in a vector args and then parse it into an i32. This code will not complie, however, because i can not call parse() on the Option value returned by nth().
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let a = args.iter().nth(1).parse::<i32>();
}
I know i could just use expect() to unwrap the value, before trying to parse it, however I do not want my code to panic. I want a to be a Result value that is an Err if either nth() or parse() fails, and otherwise is a Ok(Int). Is there a way to accomplish this in rust? Thanks.
It is quite easy if you look in the documentation for either Option or Result. The function you are thinking of is likely and_then which allows you to then provide a closure which can change the Ok type and value if filled, but otherwise leaves it unchanged when encountering an error. However, you need to do though is decide on a common error type to propagate. Since the Option<&String> needs to be turned to an error on a None value we have to choose a type to use.
Here I provide a brief example with a custom error type. I decided to use .get instead of .iter().nth(1) since it does the same thing and we might as well take advantage of the Vec since you have gone to the work of creating it.
use std::num::ParseIntError;
enum ArgParseError {
NotFound(usize),
InvalidArg(ParseIntError),
}
let args: Vec<String> = env::args().collect();
let a: Result<i32, ArgParseError> = args
.get(1) // Option<&String>
.ok_or_else(|| ArgParseError::NotFound(1)) // Result<&String, ArgParseError>
.and_then(|x: &String| {
x.parse::<i32>() // Result<i32, ParseIntError>
.map_err(|e| ArgParseError::InvalidArg(e)) // Result<i32, ArgParseError>
});
You could try the following.
use std::{env, num::ParseIntError};
enum Error {
ParseIntError(ParseIntError),
Empty,
}
fn main() {
let args: Vec<String> = env::args().collect();
let a: Option<Result<i32, ParseIntError>> = args.iter().nth(1).map(|s| s.parse::<i32>());
let a: Result<i32, Error> = match a {
Some(Ok(a)) => Ok(a),
Some(Err(e)) => Err(Error::ParseIntError(e)),
None => Err(Error::Empty),
};
}
I have my own implementation of queues (there might be some better crate in Rust, it's irrelevant, I'm trying to learn for now). It looks like this:
#[derive(Debug)]
pub struct Queue<T: Clone> {
queue: Vec<T>,
}
I am trying to use it as an argument in a different module, but I'm having some issues wrapping my head around Rust's default arguments. My module looks like this:
impl TestHandler {
pub async fn new<I>(
config: Config,
service_queue: I,
) -> Result<Self, TestError> where
I: Into<Option<Queue<isize>>>,
{
...
if service_queue.is_none() {
let mut service_queue: Queue<isize> = crate::queue![];
}
let mut handler = Self {
...
service_queue,
};
Ok(handler)
}
...
The compiler complains that:
|
47 | if service_queue.is_none() {
| ^^^^^^^ method not found in `I`
I was hoping that Option would mean exactly this. Namely that I can pass a None if I so wish. I'm not sure what Into does, I just know that without it, it won't work.
What is the correct way for me to define my custom module as an optional argument in the Handler's constructor, and check if the value was given or not?
There's a few issues here. First, you don't really need the generic argument I at all. Just accept the Option:
pub async fn new(
config: Config,
service_queue: Option<Queue<isize>>,
) -> Result<Self, TestError>
That solves the issue with is_none() but your if block is faulty -- it declares a new variable that's not used, but it appears to intend to provide a default value for the argument. That's easily done with Option::unwrap_or_else(), without an if block at all:
let service_queue = service_queue.unwrap_or_else(|| crate::queue![]);
This is a more succinct way to write:
let service_queue = match service_queue {
Some(v) => v,
None => crate::queue![],
};
I'm reading the documentation for File:
//..
let mut file = File::create("foo.txt")?;
//..
What is the ? in this line? I do not recall seeing it in the Rust Book before.
As you may have noticed, Rust does not have exceptions. It has panics, but their use for error-handling is discouraged (they are meant for unrecoverable errors).
In Rust, error handling uses Result. A typical example would be:
fn halves_if_even(i: i32) -> Result<i32, Error> {
if i % 2 == 0 {
Ok(i / 2)
} else {
Err(/* something */)
}
}
fn do_the_thing(i: i32) -> Result<i32, Error> {
let i = match halves_if_even(i) {
Ok(i) => i,
Err(e) => return Err(e),
};
// use `i`
}
This is great because:
when writing the code you cannot accidentally forget to deal with the error,
when reading the code you can immediately see that there is a potential for error right here.
It's less than ideal, however, in that it is very verbose. This is where the question mark operator ? comes in.
The above can be rewritten as:
fn do_the_thing(i: i32) -> Result<i32, Error> {
let i = halves_if_even(i)?;
// use `i`
}
which is much more concise.
What ? does here is equivalent to the match statement above with an addition. In short:
It unpacks the Result if OK
It returns the error if not, calling From::from on the error value to potentially convert it to another type.
It's a bit magic, but error handling needs some magic to cut down the boilerplate, and unlike exceptions it is immediately visible which function calls may or may not error out: those that are adorned with ?.
One example of the magic is that this also works for Option:
// Assume
// fn halves_if_even(i: i32) -> Option<i32>
fn do_the_thing(i: i32) -> Option<i32> {
let i = halves_if_even(i)?;
// use `i`
}
The ? operator, stabilized in Rust version 1.13.0 is powered by the (unstable) Try trait.
See also:
Is the question mark operator ? equivalent to the try! macro?
Why do try!() and ? not compile when used in a function that doesn't return Option or Result?
It is a postfix operator that unwraps Result<T, E> and Option<T> values.
If applied to Result<T, E>, it unwraps the result and gives you the inner value, propagating the error to the calling function.
let number = "42".parse::<i32>()?;
println!("{:?}", number); // 42
When applied to an Option<T>, it propagates None to the caller, leaving you the content of the Some branch to deal with.
let val = Some(42)?;
println!("{:?}", val); // 42
The ? operator can only be used in a function that returns Result or Option like so:
use std::num::ParseIntError;
fn main() -> Result<(), ParseIntError> {
let number = "42".parse::<i32>()?;
println!("{:?}", number);
Ok(())
}
It is a convenience offered by Rust, that eliminates boilerplate code and makes function's implementation simpler.
It is used for propagating errors. Sometimes we write code that might fail but we do not want to catch and handle error immediately. Your code will not be readable if you have too much code to handle the error in every place. Instead, if an error occurs, we might want to let our caller deal with it. We want errors to propagate up the call stack.
// file type is Result if "?" is not used
// file:Result<File,Error>
let mut file = File::create("foo.txt");
// file type is File if "?" is used
// file:File
let mut file = File::create("foo.txt")?;
// if an error occurs, code after this line will not be executed
// function will return the error
The behavior of ? depends on whether this function returns a successful result or an error result:
If it is a success, it unwraps the Result to get the success value inside. Value is assigned to the variable file
If the result is an error, the error is NOT assigned to the variable file. Error is returned by the function to the caller
Using ? same as this code
let mut file = match File::create("foo.txt") {
Err(why) => panic!("couldn't create {}: {}", display, why),
Ok(file) => file,
};
? also works similarly with the Option type. In a function that returns Option, you
can use ? to unwrap a value and return early in the case of None :
The existing answers are all great! I would like to give a small code snippet to demo the use of From::from() behand this question mark:
fn _parse(str: &str) -> Result<i32, &str> {
if let Ok(num) = str.parse::<i32>() {
Ok(num)
} else {
Err(str)
}
}
fn parse(str: &str) -> Result<(), String> {
let num = _parse(str)?;
println!("{}", num);
Ok(())
}
The use of ? in function parse() can be manually rewritten as:
fn parse(str: &str) -> Result<(), String> {
match _parse(str) {
Ok(n) => {
println!("{}", n);
Ok(())
}
Err(str) => Err(<String as From<&str>>::from(str)),
}
}
I'm new to Rust and I'm trying to figure out why Rc is behaving differently when being passed to a closure. My full code is the following:
use std::rc::Rc;
struct Something {
value: i32
}
fn main() {
let wrapped_struct = Some(Rc::new(Something { value: 1 }));
let wrapped_integer = Some(Rc::new(1));
// Case 1: This works
let works: Option<i32> = wrapped_struct.map(|i| { i.value });
// Case 2: This fails
let fails: Option<i32> = wrapped_integer.map(|i| { i });
}
The error message is:
|
13 | let fails: Option<i32> = wrapped_integer.map(|i| { i });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected i32, found struct `std::rc::Rc`
|
= note: expected type `std::option::Option<i32>`
found type `std::option::Option<std::rc::Rc<{integer}>>`
What I don't understand is why in the first closure (Case 1) I can use i as a Something (I'd expect Rc<Something>) but in the second one (Case 2) I can't use i as an i32 (I actually get an Rc<i32>).
I appreciate any pointers to the relevant documentation. Thanks a lot!
The type of the i in both closures is actually Rc<Something> and Rc<i32> respectively. Rc can be dereferenced to access its inner data, but there are places in Rust where dereferencing happens automatically, for convenience.
In the struct case, when you write i.value, it will automatically dereference i to access the field. It then returns a copy of the i32, because i32 is a Copy type. So the type of the expression i.value is i32. It's as if you wrote (*i).value, but Rust did the dereferencing for you.
In the i32 case, you are just returning the i, which still has type Rc<i32>. You can fix it by explicitly dereferencing:
wrapped_integer.map(|i| { *i });
See also:
What are Rust's exact auto-dereferencing rules?
I'm reading the documentation for File:
//..
let mut file = File::create("foo.txt")?;
//..
What is the ? in this line? I do not recall seeing it in the Rust Book before.
As you may have noticed, Rust does not have exceptions. It has panics, but their use for error-handling is discouraged (they are meant for unrecoverable errors).
In Rust, error handling uses Result. A typical example would be:
fn halves_if_even(i: i32) -> Result<i32, Error> {
if i % 2 == 0 {
Ok(i / 2)
} else {
Err(/* something */)
}
}
fn do_the_thing(i: i32) -> Result<i32, Error> {
let i = match halves_if_even(i) {
Ok(i) => i,
Err(e) => return Err(e),
};
// use `i`
}
This is great because:
when writing the code you cannot accidentally forget to deal with the error,
when reading the code you can immediately see that there is a potential for error right here.
It's less than ideal, however, in that it is very verbose. This is where the question mark operator ? comes in.
The above can be rewritten as:
fn do_the_thing(i: i32) -> Result<i32, Error> {
let i = halves_if_even(i)?;
// use `i`
}
which is much more concise.
What ? does here is equivalent to the match statement above with an addition. In short:
It unpacks the Result if OK
It returns the error if not, calling From::from on the error value to potentially convert it to another type.
It's a bit magic, but error handling needs some magic to cut down the boilerplate, and unlike exceptions it is immediately visible which function calls may or may not error out: those that are adorned with ?.
One example of the magic is that this also works for Option:
// Assume
// fn halves_if_even(i: i32) -> Option<i32>
fn do_the_thing(i: i32) -> Option<i32> {
let i = halves_if_even(i)?;
// use `i`
}
The ? operator, stabilized in Rust version 1.13.0 is powered by the (unstable) Try trait.
See also:
Is the question mark operator ? equivalent to the try! macro?
Why do try!() and ? not compile when used in a function that doesn't return Option or Result?
It is a postfix operator that unwraps Result<T, E> and Option<T> values.
If applied to Result<T, E>, it unwraps the result and gives you the inner value, propagating the error to the calling function.
let number = "42".parse::<i32>()?;
println!("{:?}", number); // 42
When applied to an Option<T>, it propagates None to the caller, leaving you the content of the Some branch to deal with.
let val = Some(42)?;
println!("{:?}", val); // 42
The ? operator can only be used in a function that returns Result or Option like so:
use std::num::ParseIntError;
fn main() -> Result<(), ParseIntError> {
let number = "42".parse::<i32>()?;
println!("{:?}", number);
Ok(())
}
It is a convenience offered by Rust, that eliminates boilerplate code and makes function's implementation simpler.
It is used for propagating errors. Sometimes we write code that might fail but we do not want to catch and handle error immediately. Your code will not be readable if you have too much code to handle the error in every place. Instead, if an error occurs, we might want to let our caller deal with it. We want errors to propagate up the call stack.
// file type is Result if "?" is not used
// file:Result<File,Error>
let mut file = File::create("foo.txt");
// file type is File if "?" is used
// file:File
let mut file = File::create("foo.txt")?;
// if an error occurs, code after this line will not be executed
// function will return the error
The behavior of ? depends on whether this function returns a successful result or an error result:
If it is a success, it unwraps the Result to get the success value inside. Value is assigned to the variable file
If the result is an error, the error is NOT assigned to the variable file. Error is returned by the function to the caller
Using ? same as this code
let mut file = match File::create("foo.txt") {
Err(why) => panic!("couldn't create {}: {}", display, why),
Ok(file) => file,
};
? also works similarly with the Option type. In a function that returns Option, you
can use ? to unwrap a value and return early in the case of None :
The existing answers are all great! I would like to give a small code snippet to demo the use of From::from() behand this question mark:
fn _parse(str: &str) -> Result<i32, &str> {
if let Ok(num) = str.parse::<i32>() {
Ok(num)
} else {
Err(str)
}
}
fn parse(str: &str) -> Result<(), String> {
let num = _parse(str)?;
println!("{}", num);
Ok(())
}
The use of ? in function parse() can be manually rewritten as:
fn parse(str: &str) -> Result<(), String> {
match _parse(str) {
Ok(n) => {
println!("{}", n);
Ok(())
}
Err(str) => Err(<String as From<&str>>::from(str)),
}
}