Matching on the result of File::create - error-handling

I am trying to write a library which must open files and I want to handle the Result type used by std::fs::File::create. I cannot figure out how to match on the return result given this wrapper function:
use std::fs::File;
use std::path::Path;
use std::fs::File;
use std::path::Path;
pub fn allocate(path:& str) -> File{
let mut file = File::create(Path::new(path));
}
which is then invoked:
mod whisper;
use std::io::Write;
fn main(){
let mut handle = whisper::allocate("./a_file.wsp");
match handle {
Ok(_) => println!("success!"),
Err(e) => println!("sorry, got {}",e),
}
return;
}
but the code doesn't compile due to a type mismatch:
Xaviers-MacBook-Pro:graphite-rust xavierlange$ cargo build
Compiling graphite-rust v0.0.1 (file:///Users/xavierlange/code/viasat/graphite-rust)
src/main.rs:8:5: 8:10 error: mismatched types:
expected `std::fs::File`,
found `core::result::Result<_, _>`
(expected struct `std::fs::File`,
found enum `core::result::Result`) [E0308]
src/main.rs:8 Ok(_) => println!("hi!"),
^~~~~
src/main.rs:9:5: 9:11 error: mismatched types:
expected `std::fs::File`,
found `core::result::Result<_, _>`
(expected struct `std::fs::File`,
found enum `core::result::Result`) [E0308]
src/main.rs:9 Err(e) => println!("sorry, got {}",e),
^~~~~~
error: aborting due to 2 previous errors
Could not compile `graphite-rust`.
The signature for std::fs::File::create is fn create<P: AsRef<Path>>(path: P) -> Result<File> so shouldn't I be expecting to "unwrap" the value using match? Why is it expecting a File value?

Let's look at a simplified version of your code, an MCVE. It's very useful to create small examples when programming, as it helps you concentrate on a single problem at a time:
use std::fs::File;
use std::path::Path;
fn allocate(path: &str) -> File {
File::create(Path::new(path))
}
fn main() {}
(I also took the liberty of aligning your code with the prevailing Rust style; I highly encourage you to learn it and love it for better community interaction!)
This gives the same error when run on the Playpen:
<anon>:5:5: 5:34 error: mismatched types:
expected `std::fs::File`,
found `core::result::Result<std::fs::File, std::io::error::Error>`
(expected struct `std::fs::File`,
found enum `core::result::Result`) [E0308]
<anon>:5 File::create(Path::new(path))
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The problem is because you have defined the return type of your function as a File, but the body of your function is returning a Result!
fn allocate(path: &str) -> File
You need to ensure that the type on your function and what the function does align. The simplest is to unwrap the result, which causes a thread panic on failure cases.
fn allocate(path: &str) -> File {
File::create(Path::new(path)).unwrap()
}
You could also return a Result of your own, and then force the caller to deal with failure (my preferred choice):
use std::io;
fn allocate(path: &str) -> io::Result<File> {
File::create(Path::new(path))
}
The other way of looking at your error is this half:
use std::fs::File;
fn allocate() -> File { unimplemented!() }
fn main() {
match allocate() {
Ok(..) => println!("OK!"),
Err(..) => println!("Bad!"),
}
}
Here, we are trying to match on a File, but File is not an enumeration with the variants Ok and Err - that would be Result! Thus, you get an error indicating such:
<anon>:7:9: 7:15 error: mismatched types:
expected `std::fs::File`,
found `core::result::Result<_, _>`
(expected struct `std::fs::File`,
found enum `core::result::Result`) [E0308]
<anon>:7 Ok(..) => println!("OK!"),
^~~~~~

Related

Why does the ? operator report the error "the trait bound NoneError: Error is not satisfied"?

The ? operator at line 9 works OK, but if I use the same logic on the same type in line 19, it blows up.
use std::error::Error;
use walkdir::WalkDir;
fn main() -> Result<(), Box<dyn Error>> {
let valid_entries = WalkDir::new("/tmp")
.into_iter()
.flat_map(|e| e)
.flat_map(|e| {
let name = e.file_name().to_str()?; // <-- this works
if name.contains(".txt") {
Some(e)
} else {
None
}
});
for entry in valid_entries {
println!("This file matches: {:?}", entry);
let name_to_str = entry.file_name().to_str()?; // <-- this blows up
// ...
}
Ok(())
}
The errors are a little cryptic for me to interpret:
error[E0277]: the trait bound `std::option::NoneError: std::error::Error` is not satisfied
--> src/main.rs:19:53
|
26 | let name_to_str = entry.file_name().to_str()?;
| ^ the trait `std::error::Error` is not implemented for `std::option::NoneError`
|
= note: required because of the requirements on the impl of `std::convert::From<std::option::NoneError>` for `std::boxed::Box<dyn std::error::Error>`
= note: required by `std::convert::From::from`
Why is the ? operator blowing up while iterating valid_entries?
The ? can be used to check-and-return any type that implements the Try trait (still unstable). The only implementations in std of those are Option<T> and Result<T, E> (plus some Future-related impls that are not relevant to this discussion). This means that you can use the ? operator in any function that returns Result<T, E> or Option<T>.
But you cannot mix-n-match those. That is, if your function returns a Result<T, E> you cannot use the ? in a value of type Option<T>. Or vice versa.
The reason your first ? works is because you are inside a flat_map() that returns Option<String> and all goes well. The second one, however, is in a function that returns a Result<(), Box<dyn Error>> so you can't use ? with an Option<String>.
The solution is simply to deal with the None in your Option<String> in another way:
Doing a match / if let Some(x) to handle the error separately.
Converting into a Result<String, Error> and use ?, for example with .ok_or(std::io::ErrorKind::InvalidData)?;.
Similar to 2, but take advantage of the impl From<&str> for Box<dyn Error> and do .ok_or("invalid file name").
Giving a default value, with Option::unwrap_or() or similar.
Doing unwrap() and panicking if None.
Ok, but what does this error mean? The idea is that actually you are able to use ? with an Option<T> that returns a Result<T, E>, as long as your E implements From<std::option::NoneError>. Unfortunately, NoneError is still unstable, too, so you can't implement From<NoneError> in your code using the stable compiler. Nevertheless, the E in your code is Box<dyn Error>, and the compiler would be happy to do the boxing as long as NoneError implements Error, but...
error[E0277]: the trait bound `std::option::NoneError: std::error::Error` is not satisfied

How do I use the Option::ok_or() method correctly?

I'm trying to understand how to use the question mark operator for error handling in Rust. I have this code:
fn main() -> Result<(), &'static str> {
let foo: i32 = Some("1")
.ok_or(Err("error 1"))?
.parse()
.or(Err("error 2"))?;
Ok(())
}
This code can not be compiled for some reason:
error[E0277]: the trait bound `&str: std::convert::From<std::result::Result<_, &str>>` is not satisfied
--> src/main.rs:2:20
|
2 | let foo: i32 = Some("1")
| ____________________^
3 | | .ok_or(Err("error 1"))?
| |_______________________________^ the trait `std::convert::From<std::result::Result<_, &str>>` is not implemented for `&str`
|
= note: required by `std::convert::From::from`
The Rust book has an example usage of the question mark operator:
use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}
In my opinion, it doesn't differ much from my example in sense of handling errors. I cannot see a reason for my code to be invalid. If the From trait should be implemented for all kinds of Result why does the code from the Rust book work fine?
Unlike or, ok_or takes an E, not a full Result<T, E> (because it wouldn't have anything to do if passed an Ok). Just pass the error string directly:
fn main() -> Result<(), &'static str> {
let foo: i32 = Some("1")
.ok_or("error 1")?
.parse()
.or(Err("error 2"))?;
Ok(())
}
The reason the error message mentions the From trait is because ? implicitly uses From to convert the expression's error type into the return value's error type. If it worked, .ok_or(Err("error 1")) would return a value of Result<&'static str, Result<_, &'static str>> (_ could be almost anything, since Err doesn't specify). The ? operator attempts to find an implementation of From that would convert Result<_, &'static str> (the expression's error type) into &'static str (the return value's error type). Since no such From implementation exists, the compiler emits an error.

fn foo() -> Result<()> throws "expected 2 type arguments"

Why isn't Result<()> allowed when compiling this bit of Rust code? Is it a breaking change between Rust editions?
fn run() -> Result<()> {
let (tx, rx) = channel();
thread::spawn(move || {
do_things_with_tx(&exit_tx);
});
match exit_rx.recv() {
Ok(result) => if let Err(reason) = result {
return Err(reason);
},
Err(e) => {
return Err(e.into());
},
}
Ok(())
}
The compiler says:
error[E0107]: wrong number of type arguments: expected 2, found 1
--> src/main.rs:1000:18
|
1000 | fn run_wifi() -> Result<()> {
| ^^^^^^^^^^ expected 2 type arguments
When I tweak the return type to Result<(), Err>, it says:
error[E0107]: wrong number of type arguments: expected 2, found 0
--> src/main.rs:1000:29
|
1000 | fn run() -> Result<(), Err> {
| ^^^ expected 2 type arguments
This is from the wifi-connect project.
The definition of Result is, and has always been, the following:
pub enum Result<T, E> {
Ok(T),
Err(E),
}
This definition is even presented in the Rust Programming language, to show how simple it is. As a generic sum type of an OK outcome and an error outcome, it always expects two type parameters, and the compiler will complain if it cannot infer them, or the list of type arguments does not have the expected length.
On the other hand, one may find many libraries and respective docs showing a Result with a single type argument, as in Result<()>. What gives?
It's still no magic. By convention, libraries create type aliases for result types at the level of a crate or module. This works pretty well because it is common for those to produce errors of the same, locally created type.
pub type Result<T> = std::result::Result<T, Error>;
Or alternatively, a definition which can still purport as the original result type.
pub type Result<T, E = Error> = std::result::Result<T, E>;
This pattern is so common that some error helper crates such as error-chain, will automatically create a result alias type for each error declared.
As such, if you are using a library that may or may not use error-chain, you are expected to assume that mentions of Result<T> are local type aliases to a domain-specific Result<T, Error>. In case of doubt, clicking on that type in the generated documentation pages will direct you to the concrete definition (in this case, the alias).
From The Rust Programming Language section The ? Operator Can Only Be Used in Functions That Return Result
use std::error::Error;
use std::fs::File;
fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("hello.txt")?;
Ok(())
}
TL;DR
use std::io::Result;
Link to the type description
Long answer
I believe that the top-voted answer given by E_net4 the comment flagger is correct. But it doesn't work if applied blindly. In both cases
this
pub type Result<T> = Result<T, Error>;
and this
pub type Result<T, E = Error> = Result<T, E>;
will give the cycle dependency error
error[E0391]: cycle detected when expanding type alias `Result`
--> src\main.rs:149:33
|
149 | pub type Result<T, E = Error> = Result<T, E>;
| ^^^^^^^^^^^^
|
= note: ...which immediately requires expanding type alias `Result` again
= note: type aliases cannot be recursive
= help: consider using a struct, enum, or union instead to break the cycle
= help: see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information
So as much as users of SO don't want to admit it, but Gabriel soft is very close to elegant solution, because that type alias
pub type Result<T> = result::Result<T, Error>;
is straight from the standard library.
Here it is, our desired Result with 1 generic argument is defined in std::io (docs). To fix the problem I added
use std::io::Result;
fn some_func() -> Result<()> {
...
}
or
use std::io;
fn some_func() -> io::Result<()> {
...
}
rustc 1.62.1
i solved my own error by making a generic Result type to handle the error
As its says it require a generic of T and E, so to simplify things, i had to follow this way
pub type Result = result::Result<T, Error>;

Convert vector of enum values into an another vector

I have the following code which generates a vector of bytes from the passed vector of enum values:
#[derive(Debug, PartialEq)]
pub enum BertType {
SmallInteger(u8),
Integer(i32),
Float(f64),
String(String),
Boolean(bool),
Tuple(BertTuple),
}
#[derive(Debug, PartialEq)]
pub struct BertTuple {
pub values: Vec<BertType>
}
pub struct Serializer;
pub trait Serialize<T> {
fn to_bert(&self, data: T) -> Vec<u8>;
}
impl Serializer {
fn enum_value_to_binary(&self, enum_value: BertType) -> Vec<u8> {
match enum_value {
BertType::SmallInteger(value_u8) => self.to_bert(value_u8),
BertType::Integer(value_i32) => self.to_bert(value_i32),
BertType::Float(value_f64) => self.to_bert(value_f64),
BertType::String(string) => self.to_bert(string),
BertType::Boolean(boolean) => self.to_bert(boolean),
BertType::Tuple(tuple) => self.to_bert(tuple),
}
}
}
// some functions for serialize bool/integer/etc. into Vec<u8>
// ...
impl Serialize<BertTuple> for Serializer {
fn to_bert(&self, data: BertTuple) -> Vec<u8> {
let mut binary: Vec<u8> = data.values
.iter()
.map(|&item| self.enum_value_to_binary(item)) // <-- what the issue there?
.collect();
let arity = data.values.len();
match arity {
0...255 => self.get_small_tuple(arity as u8, binary),
_ => self.get_large_tuple(arity as i32, binary),
}
}
}
But when compiling, I receive an error with iterating around map:
error: the trait bound `std::vec::Vec<u8>: std::iter::FromIterator<std::vec::Vec<u8>>` is not satisfied [E0277]
.collect();
^~~~~~~
help: run `rustc --explain E0277` to see a detailed explanation
note: a collection of type `std::vec::Vec<u8>` cannot be built from an iterator over elements of type `std::vec::Vec<u8>`
error: aborting due to previous error
error: Could not compile `bert-rs`.
How can I fix this issue with std::iter::FromIterator?
The problem is that enum_value_to_binary returns a Vec<u8> for each element in values. So you end up with an Iterator<Item=Vec<u8>> and you call collect::<Vec<u8>>() on that, but it doesn't know how to flatten the nested vectors. If you want all the values to be flattened into one Vec<u8>, then you should use flat_map instead of map:
let mut binary: Vec<u8> = data.values
.iter()
.flat_map(|item| self.enum_value_to_binary(item).into_iter())
.collect();
Or, slightly more idiomatic and performant, you can just have enum_value_to_binary return an iterator directly.
Also, the iter method returns an Iterator<Item=&'a T>, which means you are just borrowing the elements, but self.enum_value_to_binary wants to take ownership over the value. There's a couple of ways to fix that. One option would be to use into_iter instead of iter, which will give you the elements by value. If you do that, you'll move the arity variable up to before the binary variable, since creating the binary variable will take ownership (move) data.values.
The other option would be to change self.enum_value_to_binary to take it's argument by reference.
Also possible that you meant for the type of binary to actually be Vec<Vec<u8>>.

Figuring out return type of closure

I'm having troubles figuring out the type signature of the fn filter function in following example.
The Node and Descendant definition is just there for syntax . It's not meant to do anything!
use std::iter::Filter;
#[derive(Clone)]
pub struct Node<'a> {
s: &'a str,
}
pub struct Descendants<'a>{
iter: Node<'a>
}
impl<'a> Iterator for Descendants<'a> {
type Item = Node<'a>;
fn next(&mut self) -> Option<Node<'a>> {
Some(Node {s: self.iter.s})
}
}
impl<'a> Node<'a> {
pub fn descendants(&self) -> Descendants<'a> {
Descendants{ iter: Node{s: self.s} }
}
pub fn filter(&self, criteria: &str) -> Filter<Descendants<'a>, fn(&'a Node<'a>)->bool > {
self.descendants()
.filter(|node| node.s == "meh")
}
}
fn main() {
let doc = Node{s: "str"};
}
(Playground link)
The error I get is following:
<anon>:27:28: 27:34 error: the type of this value must be known in this context
<anon>:27 .filter(|node| node.s == "meh")
^~~~~~
<anon>:27:21: 27:43 error: mismatched types:
expected `fn(&Node<'_>) -> bool`,
found `[closure <anon>:27:21: 27:43]`
(expected fn pointer,
found closure) [E0308]
<anon>:27 .filter(|node| node.s == "meh")
^~~~~~~~~~~~~~~~~~~~~~
<anon>:27:14: 27:44 error: type mismatch: the type `fn(&Node<'_>) -> bool` implements the trait `core::ops::FnMut<(&Node<'_>,)>`, but the trait `for<'r> core::ops::FnMut<(&'r Node<'_>,)>` is required (expected concrete lifetime, found bound lifetime parameter ) [E0281]
<anon>:27 .filter(|node| node.s == "meh")
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<anon>:27:14: 27:44 error: type mismatch resolving `for<'r> <fn(&Node<'_>) -> bool as core::ops::FnOnce<(&'r Node<'_>,)>>::Output == bool`:
expected bound lifetime parameter ,
found concrete lifetime [E0271]
<anon>:27 .filter(|node| node.s == "meh")
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 4 previous errors
playpen: application terminated with error code 101
When I as per this question Correct way to return an Iterator? I tried to replace pub fn filter(&self, criteria: &str) -> Filter<Descendants<'a>, fn(&'a Node<'a>)->bool > with pub fn filter(&self, criteria: &str) -> () I get
<anon>:26:9: 27:44 error: mismatched types:
expected `()`,
found `core::iter::Filter<Descendants<'_>, [closure <anon>:27:21: 27:43]>`
What I'm supposed to replace closure with?
Alternatively, if it's too hard and finicky to return a Filter, how do I write the Wrapper for fn filter() return type?
I clearly remember that this was answered before a few times (I even wrote about it in an answer a few minutes before) but I can't find a link now, so here it goes.
The problem with your code is that you use a closure as filter() argument:
.filter(|node| node.s == "meh")
Unboxed closures in Rust are implemented as anonymous types which, naturally, can't be named, so there is no way to write a signature of a function which returns an iterator which uses a closure. That's what the error message you're getting is about:
expected `fn(&Node<'_>) -> bool`,
found `[closure <anon>:27:21: 27:43]`
(expected fn pointer,
found closure) [E0308]
There are several ways around this, one of them is to use trait objects:
pub fn filter<'b>(&'b self, criteria: &'b str) -> Box<Iterator<Item=Node<'a>+'b>>
{
Box::new(self.descendants().filter(move |node| node.s == criteria))
}
Given that your closure has a non-empty environment, this is the only way for your code to work. If your closure didn't capture anything, you could use a static function whose type can be written out:
pub fn filter(&self) -> Filter<Descendants<'a>, fn(&Node<'a>) -> bool> {
fn filter_fn<'b>(node: &Node<'b>) -> bool {
node.s == "meh"
}
self.descendants().filter(filter_fn)
}