I am trying to return an error with a formatted message.
let msg = format!("wrong num of dimensions: {}", dimensions.len()).to_string();
return Err(msg);
dimensions is a Vec<i32> that is supposed to have a length of 3. If it doesn't, I want to raise this error. However, I am getting this compiler error:
Compiling advent02 v0.1.0 (file:///home/garrett/notes/rust/advent/advent02)
src/main.rs:19:24: 19:27 error: mismatched types:
expected `&str`,
found `collections::string::String`
(expected &-ptr,
found struct `collections::string::String`) [E0308]
src/main.rs:19 return Err(msg);
^~~
It looks to me like it wants an &str type when I am giving it a String type. I have tried to do this from this example but get borrow errors because the returned string borrow goes out of scope.
I don't understand because this page is littered with examples that do Err(err.to_string()), and they don't seem to have a problem.
The return type of your function is Result<T, &str>, that's why the compiler is complaining. You create a String instance, take a reference to it and try to return. When the function returns, the String instance is freed and your returned reference would be invalid (a dangling pointer).
I'd change the signature of the function to return a Result<T, String>. That way, you move the string out of the function without freeing it.
Here is a complete Rust program that shows the behavior you wanted:
fn test() -> Result<u32, String> {
let msg = format!("Error code {}.", 123).to_string();
return Err(msg);
}
fn main() {
match test() {
Ok(num) => println!("Okay: {}", num),
Err(msg) => println!("Error: {}", msg),
};
}
This compiles without warnings or errors and prints "Error: Error code 123." when run.
The important thing here is to declare the return type of the function that is returning the error code. If you don't declare the return type, then the Rust compiler apparently does a bad job of figuring out what the return type should be, and you get errors (but the errors I get from rustc 1.5.0 are different from yours). This might actually be a bug in the Rust compiler; you could report this to the developers of the language and see what they have to say.
The Err thing that you used is just a type constructor for std::result::Result. So the return type of the function should be some type of Result.
Related
The Rust compiler is usually able to infer the type of an expression that is returned from a closure:
fn main() {
let a_closure = |num|{
num+1.0
};
println!("{}", a_closure(1.0));
}
But the compiler is unable to infer the type when I define the same closure using a return statement:
fn main() {
let a_closure = |num|{
return num+1.0
};
println!("{}", a_closure(1.0));
}
/*
error[E0308]: mismatched types
--> src/main.rs:3:9
|
3 | return num+1.0
| ^^^^^^^^^^^^^^ expected `()`, found `f64`
*/
I'm surprised that Rust can't infer the type here: is it possible to use a return statement in a closure without preventing the compiler from inferring its return type?
This is due to the lack of a semicolon. When you have no semicolon, the last expression is returned, and this last expression is return num + 1.0. Since a return statement makes the program jump somewhere, else, it's value can be anything, for example:
fn main() {
let a: String = return;
}
However, if the compiler sees no direct type assigned to it, it will pick the type () as the value of the return statement.
So what happened is:
The compiler sees the last expression in the closure, and assigns it the type () by default.
The compiler then sees an explicit return inside the closure, which returns the type i32.
So since there are two attempts to return from the closure, and they each return different types, that's a type mismatch.
I've been following a Rust tutorial where two versions of a function are purported to generate the same results:
Version 1:
pub fn get_transactions(fname:&str) -> Result<Vec<Transaction>,String> {
let s = match std::fs::read_to_string(fname){
Ok(v)=>v,
Err(e)=> return Err(e.to_string()),
};
let t:Vec<Transaction> = match serde_json::from_str(&s) {
Ok(v)=>v,
Err(e)=> return Err(e.to_string()),
};
Ok(t)
}
Version 2:
fn get_transactions_b(fname:&str) -> Result<Vec<Transaction>,String> {
std::fs::read_to_string(fname)
.map_err(|e| e.to_string())
.and_then(|ld| serde_json::from_str(&ld) )
.map_err(|e| e.to_string())
}
However, I get the following message for version 2:
mismatched types
expected struct std::string::String, found struct serde_json::error::Error
note: expected enum std::result::Result<_, std::string::String>
found enum std::result::Result<_, serde_json::error::Error>
help: try using a variant of the expected enum: _::_serde::export::Ok(serde_json::from_str(&ld)),
which I been unable to make head or tail out of:
Hovering over ld closure argument |ld| in VS Code it says it's of type std::string::String
Hovering over the ld in the from_str(&ld) I get the message.
Now I understand a Result is comprised of an Ok and an Err, but I thought the combinator chaining would have worked.
The compiler suggested fix doesn't work either.
(or make sense): What is the _::_ all about?
Why would the Ok not be inside the from_str?
What would you have to do to make version 2 work?
Here's the Result with the methods for combinators in the Rust docs.
Cargo.toml
[dependencies]
serde = "1.0.115"
serde_derive = "1.0.115"
serde_json = "1.0.57"
cargo 1.45.1
rustc 1.45.2
The problem comes from the and_then call.
You are not allowed to change the error type when calling and_then.
So your function there should return a Result<Vec<Transaction>, String>. However, the error type returned by serde_json::from_str is a serde_json::error::Error.
You can fix it like this:
std::fs::read_to_string(fname)
.map_err(|e| e.to_string())
.and_then(|ld| serde_json::from_str(&ld).map_err(|e| e.to_string()))
Notice the call to map_err is now inside the function passed to and_then.
Rust's str class has a parse method that returns a FromStr object. parse is templated, and so the type that's being parsed from the str can be manually specified, e.g. "3".parse::<i32>() evaluates to (a Result object containing) the 32-bit int 3.
But failing to specify the type does not seem to be an error in itself. Instead, I get an error when trying to print the resulting (generic/unspecified) FromStr object:
let foo = "3".parse();
match foo
{
Ok(m) => println!("foo: {}", m),
Err(e) => println!("error! {}", e)
}
This does not give an error on the first line; instead, I get the following error:
<anon>:24:12: 24:13 error: unable to infer enough type information about `_`; type annotations or generic parameter binding required [E0282]
<anon>:24 Ok(m) => println!("foo: {}", m),
(Here, line 24 is the line with the Ok(m).)
So what is m here? Or is the "unable to infer enough type information" error actually due to the fact that parse in fact can't be called without a type specifier, and the compiler just doesn't catch the error until the first line where the resulting Ok type is actually used?
Rust's str class has a parse method that returns a FromStr object.
Stop right here, this is your error.
parse does not return a FromStr object; FromStr is a trait which can be thought of as an abstract class if you come from an OO background, and you cannot return an object with an abstract type: it's abstract!
What parse does return, thus, is an instance of some type T which must implement the FromStr interface.
But failing to specify the type does not seem to be an error in itself. Instead, I get an error when trying to print the resulting (generic/unspecified) FromStr object
Because there cannot be such generic/unspecific FromStr object. A concrete type must be inferred (from context) or explicitly spelled out, and this type must implement FromStr.
So what is m here?
Only you know what it should be, the compiler does not, and thus complain that it does not know what to do :)
Or is the "unable to infer enough type information" error actually due to the fact that parse in fact can't be called without a type specifier, and the compiler just doesn't catch the error until the first line where the resulting Ok type is actually used?
Basically.
Except that it's not so much that the compiler doesn't catch the error until the first line where the resulting Ok is used, and more that the compiler considers the full function at once when inferring types. From the point of view of the compiler, whether the actual clue to infer the type comes immediately or comes 50 lines down does not matter, it only needs to be present in the current function body.
It might lead to the complaint about the lack of type originating in an odd place from the developer point of view; this is one of the downfalls of type inference. On the other hand, the compiler just cannot know where YOU would prefer to put the annotation. There are after all many possibilities:
// Example 1: immediately specifying the type
fn main() {
let foo = "3".parse::<i32>();
match foo
{
Ok(m) => println!("foo: {}", m),
Err(e) => println!("error! {}", e)
}
}
// Example 2: partially specifying the result type
// Note: the "_" is deduced to be std::num::ParseIntError because
// this is how `FromStr::Err` is defined for `i32`.
fn main() {
let foo: Result<i32, _> = "3".parse();
match foo
{
Ok(m) => println!("foo: {}", m),
Err(e) => println!("error! {}", e)
}
}
// Example 3: specifying the result type of unwrapping
fn doit() -> Result<(), std::num::ParseIntError> {
let foo: i32 = try!("3".parse());
println!("foo: {}", foo);
Ok(())
}
fn main() {
match doit()
{
Ok(_) => (),
Err(e) => println!("error! {}", e)
}
}
// Example 4: letting the type be inferred from a function call
fn callit(f: i32) {
println!("f: {}", f);
}
fn main() {
let foo = "3".parse();
match foo
{
Ok(m) => callit(m),
Err(e) => println!("error! {}", e)
}
}
It's just not clear what m is here, as there isn't enough information to say. Is it an i32? A u64? Nobody, including Rust, can know.
You need to do something to help figure out what type it is. Either pass it to a function expecting a specific type, or annotate it such that it can be determined what type it should be.
I'm trying to return an error Result with a &'static str.
impl Worker {
fn get_task_by_name(&self, name: String) -> Result<Box<Task>, &'static str> {
Err("Task not found!");
}
}
It outputs the following error:
src/lib.rs:84:5: 84:8 error: unable to infer enough type information about `_`; type annotations or generic parameter binding required [E0282]
src/lib.rs:84 Err("Task not found!");
^~~
What could be the problem here?
You have a spurious semicolon after the Err(...). You're telling the compiler to throw away the value you construct and return () instead. Of course, it doesn't get as far as telling you the return type is wrong: it's more immediately confused by the fact that you've constructed a Result<T, E>::Err(E) without telling it what T is.
The code below best describes the issue.
use std::iter::IntoIterator;
fn iterate<I: IntoIterator<Item=String>>(v: I) {
}
// iterate(&["foo".to_string()])
// error: type mismatch resolving `<&[collections::string::String; 1] as core::iter::IntoIterator>::Item == collections::string::String`:
// expected &-ptr,
// found struct `collections::string::String` [E0271]
// iterate(["foo".to_string()].iter())
// type mismatch resolving `<core::slice::Iter<'_, collections::string::String> as core::iter::IntoIterator>::Item == collections::string::String`:
// expected &-ptr,
// found struct `collections::string::String` [E0271]
// This works !
iterate(vec!["foo".to_string()])
How can I iterate anything (with a given item type) generically?
Additional Notes
The intention is to allow users of such a function to pass in anything that can be iterated, or converted into an iterator.
Also I have the feeling that the actual issue is not really described in the compiler error - as the type it sees seems to be different from what it shows.
I am using rustc 1.0.0-nightly (522d09dfe 2015-02-19) (built 2015-02-19)
Let's look at what the types are for your first case:
for i in &["foo".to_string()] {
let () = i;
// expected `&collections::string::String`,
// found `()`
}
That is, the type of your iteration variable is &String, not on String, as your function wants. The same thing happens for your second case. The third case works:
for i in vec!["foo".to_string()] {
let () = i;
// expected `collections::string::String`,
// found `()`
}
We can look at the implementation for IntoIter for arrays and for all 3 forms of Vec. Note that the implementation for Vec<T> consumes the vector, whereas the one for &Vec<T> must return an iterator over references to the elements.
You can't consume an array, so that iterator must always return references.
Here's an example that uses the AsRef trait that should do what you want:
use std::iter::IntoIterator;
fn iterate<I, S>(v: I)
where I: IntoIterator<Item=S>,
S: AsRef<str>
{}
fn main() {
iterate(&["foo"]);
iterate(&["foo".to_string()]);
iterate(["foo".to_string()].iter());
iterate(vec!["foo".to_string()]);
iterate(&vec!["foo".to_string()]);
}
This says that we expect any concrete type that implements the trait Iterator. That iterator must yield a type that implements the trait AsRef<str>, allowing us to pass in {arrays,vectors,slices} of {String,&str}.