This code looks like it would work fine to me, but the rust borrow checker doesn't like it:
extern crate rustbox;
use std::error::Error;
use std::default::Default;
use rustbox::{Color, RustBox};
use rustbox::Key;
use std::fs::File;
use std::env;
use std::io::BufReader;
use std::io::BufRead;
fn display_screenful(rb: &RustBox, fr: BufReader<&'static File>, offset: usize) {
for (rline, idx) in fr.lines().zip(0..).skip(offset).take(rb.height()) {
match rline {
Ok(line) => (*rb).print(0, idx, rustbox::RB_NORMAL, Color::White, Color::Black, &line),
Err(_) => (*rb).print(0, idx, rustbox::RB_NORMAL, Color::White, Color::Black, "")
}
}
}
fn main() {
let rustbox = match RustBox::init(Default::default()) {
Ok(v) => v,
Err(e) => panic!(e),
};
let path = env::args().nth(1).unwrap();
let file = match File::open(&path) {
Ok(file) => file,
Err(e) => panic!(e)
};
let file_reader = BufReader::new(&file);
display_screenful(&rustbox, file_reader, 0);
rustbox.present();
loop {
match rustbox.poll_event(false) {
Ok(rustbox::Event::KeyEvent(key)) => {
match key {
Some(Key::Char('q')) => { break; }
Some(Key::Char(' ')) => {
display_screenful(&rustbox, file_reader, rustbox.height());
rustbox.present();
}
_ => { }
}
},
Err(e) => panic!("{}", e.description()),
_ => { }
}
}
}
I guess I could not use a separate function, and use two for loop parts, instead, but that isn't idiomatic Rust, nor is it good coding practice. In fact, I've tried that, but it just tells me that I'm using a moved value. Here are some errors that I'm getting:
Compiling rusted v0.1.0 (file:///Users/christopherdumas/rusted)
src/main.rs:34:39: 34:43 error: `file` does not live long enough
src/main.rs:34 let file_reader = BufReader::new(&file);
^~~~
note: reference must be valid for the static lifetime...
src/main.rs:33:7: 55:2 note: ...but borrowed value is only valid for the block suffix following statement 2 at 33:6
src/main.rs:33 };
src/main.rs:34 let file_reader = BufReader::new(&file);
src/main.rs:35
src/main.rs:36 display_screenful(&rustbox, file_reader, 0);
src/main.rs:37 rustbox.present();
src/main.rs:38
...
src/main.rs:45:53: 45:64 error: use of moved value: `file_reader` [E0382]
src/main.rs:45 display_screenful(&rustbox, file_reader, rustbox.height());
^~~~~~~~~~~
src/main.rs:45:53: 45:64 help: run `rustc --explain E0382` to see a detailed explanation
src/main.rs:36:33: 36:44 note: `file_reader` moved here because it has type `std::io::buffered::BufReader<&'static std::fs::File>`, which is non-copyable
src/main.rs:36 display_screenful(&rustbox, file_reader, 0);
^~~~~~~~~~~
error: aborting due to 2 previous errors
Could not compile `rusted`.
To learn more, run the command again with --verbose.
Firstly, you shouldn't ask for a BufReader<&'static File>. You can't provide one. Ask instead for a BufReader<&'a File> for some lifetime 'a.
fn display_screenful<'a>(rb: &RustBox, fr: BufReader<&'a File>, offset: usize)
or, for short,
fn display_screenful(rb: &RustBox, fr: BufReader<&File>, offset: usize)
That's not enough either - you then end up moving file_reader into the function. You should borrow instead:
fn display_screenful(rb: &RustBox, fr: &mut BufReader<&File>, offset: usize)
and then it compiles.
Related
Why does this code not compile?
use std::{fs, path::Path};
fn main() {
let dir = Path::new("../FileSystem");
if !dir.is_dir() {
println!("Is not a directory");
return;
}
for item in try!(fs::read_dir(dir)) {
let file = match item {
Err(e) => {
println!("Error: {}", e);
return;
}
Ok(f) => f,
};
println!("");
}
println!("Done");
}
This is the error I get
error[E0308]: mismatched types
--> src/main.rs:11:17
|
11 | for item in try!(fs::read_dir(dir)) {
| ^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
|
= note: expected type `()`
found type `std::result::Result<_, _>`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
I also tried the question mark operator:
for item in fs::read_dir(dir)? {
Which had a different error:
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> src/main.rs:11:17
|
11 | for item in fs::read_dir(dir)? {
| ^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
Previous versions of Rust had a similar error about std::ops::Carrier
Should I avoid try!() and ?? What is the best way to handle errors? Mostly I do it like this:
match error_prone {
Err(e) => {
println!("Error: {}", e);
return;
},
Ok(f) => f,
};
But if I have to use that in a for loop, it's a complete mess
for i in match error_prone {
// match code
} {
// loop code
}
try! is a macro that returns Errs automatically; ? is syntax that does mostly the same thing, but it works with any type that implements the Try trait.
As of Rust 1.22.0, Option implements Try, so it can be used with ?. Before that, ? could only be used in functions that return a Result. try! continues to only work with Results.
As of Rust 1.26.0, main is allowed to return a value that implements Termination. Before that, it doesn't return any value.
As of Rust 1.26.0
Your original code works if you mark main as returning a Result and then return Ok(()) in all the "success" cases:
use std::{fs, io, path::Path};
fn main() -> Result<(), io::Error> {
let dir = Path::new("../FileSystem");
if !dir.is_dir() {
println!("Is not a directory");
return Ok(());
}
for item in fs::read_dir(dir)? {
let file = match item {
Err(e) => {
println!("Error: {}", e);
return Ok(());
}
Ok(f) => f,
};
println!("");
}
println!("Done");
Ok(())
}
Before that
This is how you might transform your code to use ?:
use std::{error::Error, fs, path::Path};
fn print_dir_contents() -> Result<String, Box<Error>> {
let dir = Path::new("../FileSystem");
if !dir.is_dir() {
return Err(Box::from("Is not a directory!"));
}
for entry in fs::read_dir(dir)? {
let path = entry?.path();
let file_name = path.file_name().unwrap();
println!("{}", file_name.to_string_lossy());
}
Ok("Done".into())
}
fn main() {
match print_dir_contents() {
Ok(s) => println!("{}", s),
Err(e) => println!("Error: {}", e.to_string()),
}
}
There's a lot of error handling here that you might not expect - other languages don't tend to require it! But they exist in other languages - Rust just makes you know it. Here are the errors:
entry?
IO errors can happen during iteration.
path.file_name().unwrap()
Not all paths have file names. We can unwrap this because read_dir won't give us a path without a file name.
file_name.to_string_lossy()
You can also to_str and throw an error, but it's nicer to do this. This error exists because not all file names are valid Unicode.
try! and ? throw errors into the return value, converting them to Box::Error. It's actually more reasonable to return an amalgamated error of all the things that can go wrong. Luckily io::Error is just the right type:
use std::io;
// ...
fn print_dir_contents() -> Result<String, io::Error> {
// ...
if !dir.is_dir() {
return Err(io::Error::new(io::ErrorKind::Other, "Is not a directory!"));
}
// ...
}
Frankly, though, this check is already in fs::read_dir, so you can actually just remove the if !dis.is_dir altogether:
use std::{fs, io, path::Path};
fn print_dir_contents() -> Result<String, io::Error> {
let dir = Path::new("../FileSystem");
for entry in fs::read_dir(dir)? {
let path = entry?.path();
let file_name = path.file_name().unwrap();
println!("{}", file_name.to_string_lossy());
}
Ok("Done".into())
}
fn main() {
match print_dir_contents() {
Ok(s) => println!("{}", s),
Err(e) => println!("Error: {}", e.to_string()),
}
}
The ques_in_main RFC got merged recently. Once it's completed, the syntax in the question will indeed compile just fine and work as intended, provided the try!() calls are replaced with the ? operator.
As of Rust 1.26, Rust supports a return value from main(), and thus supports the use of the error-check operator ? (or equivalently the try!() macro) in main() when main() is defined to return a Result:
extern crate failure;
use failure::Error;
use std::fs::File;
type Result<T> = std::result::Result<T, Error>;
fn main() -> Result<()> {
let mut _file = File::open("foo.txt")?; // does not exist; returns error
println!("the file is open!");
Ok(())
}
The above compiles and returns a file not found error (assuming foo.txt does not exist in the local path).
Rust playground example
Veedrac's answer helped me too, although the OP's question is slightly different. While reading the Rust documentation, I saw this snippet:
use std::fs::File;
use std::io::prelude::*;
let mut file = File::open("foo.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
assert_eq!(contents, "Hello, world!");
Though in the Rust Book they point out the centrality of the main function, if you run this inside it you'll get a similar error. If you wrap the code inside a function handling the errors the aforementioned snippet works:
use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
fn print_file_content() -> Result<String, Box<Error>> {
let mut f = File::open("foo.txt")?;
let mut contents = String::new();
f.read_to_string(&mut contents)?;
println!("The content: {:?}", contents);
Ok("Done".into())
}
fn main() {
match print_file_content() {
Ok(s) => println!("{}", s),
Err(e) => println!("Error: {}", e.to_string()),
}
}
P.S. I'm learning Rust so these snippets are not intended as good Rust coding :)
I have the following tree structure:
use std::cell::RefCell;
use std::rc::Rc;
use std::cmp;
use std::cmp::Ordering;
type AVLTree<T> = Option<Rc<RefCell<TreeNode<T>>>>;
#[derive(Debug, PartialEq, Clone)]
struct TreeSet<T: Ord> {
root: AVLTree<T>,
}
impl<T: Ord> TreeSet<T> {
fn new() -> Self {
Self {
root: None
}
}
fn insert(&mut self, value: T) -> bool {
let current_tree = &mut self.root;
while let Some(current_node) = current_tree {
let node_key = ¤t_node.borrow().key;
match node_key.cmp(&value) {
Ordering::Less => { let current_tree = &mut current_node.borrow_mut().right; },
Ordering::Equal => {
return false;
}
Ordering::Greater => { let current_tree = &mut current_node.borrow_mut().left; },
}
}
*current_tree = Some(Rc::new(RefCell::new(TreeNode {
key: value,
left: None,
right: None,
parent: None
})));
true
}
}
#[derive(Clone, Debug, PartialEq)]
struct TreeNode<T: Ord> {
pub key: T,
pub parent: AVLTree<T>,
left: AVLTree<T>,
right: AVLTree<T>,
}
fn main() {
let mut new_avl_tree: TreeSet<u32> = TreeSet::new();
new_avl_tree.insert(3);
new_avl_tree.insert(5);
println!("Tree: {:#?}", &new_avl_tree);
}
Building with cargo build is fine, but when I run cargo run, I got the below error:
thread 'main' panicked at 'already borrowed: BorrowMutError', src\libcore\result.rs:1165:5
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace. error: process didn't
exit successfully: target\debug\avl-tree.exe (exit code: 101)
If i just call insert(3), it will be fine and my tree gets printed correctly. However, if I insert(5) after insert(3), I will get that error.
How do I fix that?
Manually implementing data structures such as linked list, tree, graph are not task for novices, because of memory safety rules in language. I suggest you to read Too Many Linked Lists tutorial, which discusses how to implement safe and unsafe linked lists in Rust right way.
Also read about name shadowing.
Your error is that inside a cycle you try to borrow mutable something which is already borrowed as immutable.
let node_key = ¤t_node.borrow().key; // Borrow as immutable
match node_key.cmp(&value) {
Ordering::Less => { let current_tree = &mut current_node.borrow_mut().right; }, // Create a binding which will be immediately deleted and borrow as mutable.
And I recommend you to read Rust book to learn rust.
First let us correct your algorithm. The following lines are incorrect:
let current_tree = &mut current_node.borrow_mut().right;
...
let current_tree = &mut current_node.borrow_mut().left;
Both do not reassign a value to current_tree but create a new (unused) one (#Inline refers to it as Name shadowing). Remove the let and make current_tree mut.
Now we get a compiler error temporary value dropped while borrowed. Probably the compiler error message did mislead you. It tells you to use let to increase the lifetime, and this would be right if you used the result in the same scope, but no let can increase the lifetime beyond the scope.
The problem is that you cannot pass out a reference to a value owned by a loop (as current_node.borrow_mut.right). So it would be better to use current_tree as owned variable. Sadly this means that many clever tricks in your code will not work any more.
Another problem in the code is the multiple borrow problem (your original runtime warning is about this). You cannot call borrow() and borrow_mut() on the same RefCell without panic(that is the purpose of RefCell).
So after finding the problems in your code, I got interested in how I would write the code. And now that it is written, I thought it would be fair to share it:
fn insert(&mut self, value: T) -> bool {
if let None = self.root {
self.root = TreeSet::root(value);
return true;
}
let mut current_tree = self.root.clone();
while let Some(current_node) = current_tree {
let mut borrowed_node = current_node.borrow_mut();
match borrowed_node.key.cmp(&value) {
Ordering::Less => {
if let Some(next_node) = &borrowed_node.right {
current_tree = Some(next_node.clone());
} else {
borrowed_node.right = current_node.child(value);
return true;
}
}
Ordering::Equal => {
return false;
}
Ordering::Greater => {
if let Some(next_node) = &borrowed_node.left {
current_tree = Some(next_node.clone());
} else {
borrowed_node.left = current_node.child(value);
return true;
}
}
};
}
true
}
//...
trait NewChild<T: Ord> {
fn child(&self, value: T) -> AVLTree<T>;
}
impl<T: Ord> NewChild<T> for Rc<RefCell<TreeNode<T>>> {
fn child(&self, value: T) -> AVLTree<T> {
Some(Rc::new(RefCell::new(TreeNode {
key: value,
left: None,
right: None,
parent: Some(self.clone()),
})))
}
}
One will have to write the two methods child(value:T) and root(value:T) to make this compile.
Why does this code not compile?
use std::{fs, path::Path};
fn main() {
let dir = Path::new("../FileSystem");
if !dir.is_dir() {
println!("Is not a directory");
return;
}
for item in try!(fs::read_dir(dir)) {
let file = match item {
Err(e) => {
println!("Error: {}", e);
return;
}
Ok(f) => f,
};
println!("");
}
println!("Done");
}
This is the error I get
error[E0308]: mismatched types
--> src/main.rs:11:17
|
11 | for item in try!(fs::read_dir(dir)) {
| ^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
|
= note: expected type `()`
found type `std::result::Result<_, _>`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
I also tried the question mark operator:
for item in fs::read_dir(dir)? {
Which had a different error:
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> src/main.rs:11:17
|
11 | for item in fs::read_dir(dir)? {
| ^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
Previous versions of Rust had a similar error about std::ops::Carrier
Should I avoid try!() and ?? What is the best way to handle errors? Mostly I do it like this:
match error_prone {
Err(e) => {
println!("Error: {}", e);
return;
},
Ok(f) => f,
};
But if I have to use that in a for loop, it's a complete mess
for i in match error_prone {
// match code
} {
// loop code
}
try! is a macro that returns Errs automatically; ? is syntax that does mostly the same thing, but it works with any type that implements the Try trait.
As of Rust 1.22.0, Option implements Try, so it can be used with ?. Before that, ? could only be used in functions that return a Result. try! continues to only work with Results.
As of Rust 1.26.0, main is allowed to return a value that implements Termination. Before that, it doesn't return any value.
As of Rust 1.26.0
Your original code works if you mark main as returning a Result and then return Ok(()) in all the "success" cases:
use std::{fs, io, path::Path};
fn main() -> Result<(), io::Error> {
let dir = Path::new("../FileSystem");
if !dir.is_dir() {
println!("Is not a directory");
return Ok(());
}
for item in fs::read_dir(dir)? {
let file = match item {
Err(e) => {
println!("Error: {}", e);
return Ok(());
}
Ok(f) => f,
};
println!("");
}
println!("Done");
Ok(())
}
Before that
This is how you might transform your code to use ?:
use std::{error::Error, fs, path::Path};
fn print_dir_contents() -> Result<String, Box<Error>> {
let dir = Path::new("../FileSystem");
if !dir.is_dir() {
return Err(Box::from("Is not a directory!"));
}
for entry in fs::read_dir(dir)? {
let path = entry?.path();
let file_name = path.file_name().unwrap();
println!("{}", file_name.to_string_lossy());
}
Ok("Done".into())
}
fn main() {
match print_dir_contents() {
Ok(s) => println!("{}", s),
Err(e) => println!("Error: {}", e.to_string()),
}
}
There's a lot of error handling here that you might not expect - other languages don't tend to require it! But they exist in other languages - Rust just makes you know it. Here are the errors:
entry?
IO errors can happen during iteration.
path.file_name().unwrap()
Not all paths have file names. We can unwrap this because read_dir won't give us a path without a file name.
file_name.to_string_lossy()
You can also to_str and throw an error, but it's nicer to do this. This error exists because not all file names are valid Unicode.
try! and ? throw errors into the return value, converting them to Box::Error. It's actually more reasonable to return an amalgamated error of all the things that can go wrong. Luckily io::Error is just the right type:
use std::io;
// ...
fn print_dir_contents() -> Result<String, io::Error> {
// ...
if !dir.is_dir() {
return Err(io::Error::new(io::ErrorKind::Other, "Is not a directory!"));
}
// ...
}
Frankly, though, this check is already in fs::read_dir, so you can actually just remove the if !dis.is_dir altogether:
use std::{fs, io, path::Path};
fn print_dir_contents() -> Result<String, io::Error> {
let dir = Path::new("../FileSystem");
for entry in fs::read_dir(dir)? {
let path = entry?.path();
let file_name = path.file_name().unwrap();
println!("{}", file_name.to_string_lossy());
}
Ok("Done".into())
}
fn main() {
match print_dir_contents() {
Ok(s) => println!("{}", s),
Err(e) => println!("Error: {}", e.to_string()),
}
}
The ques_in_main RFC got merged recently. Once it's completed, the syntax in the question will indeed compile just fine and work as intended, provided the try!() calls are replaced with the ? operator.
As of Rust 1.26, Rust supports a return value from main(), and thus supports the use of the error-check operator ? (or equivalently the try!() macro) in main() when main() is defined to return a Result:
extern crate failure;
use failure::Error;
use std::fs::File;
type Result<T> = std::result::Result<T, Error>;
fn main() -> Result<()> {
let mut _file = File::open("foo.txt")?; // does not exist; returns error
println!("the file is open!");
Ok(())
}
The above compiles and returns a file not found error (assuming foo.txt does not exist in the local path).
Rust playground example
Veedrac's answer helped me too, although the OP's question is slightly different. While reading the Rust documentation, I saw this snippet:
use std::fs::File;
use std::io::prelude::*;
let mut file = File::open("foo.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
assert_eq!(contents, "Hello, world!");
Though in the Rust Book they point out the centrality of the main function, if you run this inside it you'll get a similar error. If you wrap the code inside a function handling the errors the aforementioned snippet works:
use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
fn print_file_content() -> Result<String, Box<Error>> {
let mut f = File::open("foo.txt")?;
let mut contents = String::new();
f.read_to_string(&mut contents)?;
println!("The content: {:?}", contents);
Ok("Done".into())
}
fn main() {
match print_file_content() {
Ok(s) => println!("{}", s),
Err(e) => println!("Error: {}", e.to_string()),
}
}
P.S. I'm learning Rust so these snippets are not intended as good Rust coding :)
Why does this code not compile?
use std::{fs, path::Path};
fn main() {
let dir = Path::new("../FileSystem");
if !dir.is_dir() {
println!("Is not a directory");
return;
}
for item in try!(fs::read_dir(dir)) {
let file = match item {
Err(e) => {
println!("Error: {}", e);
return;
}
Ok(f) => f,
};
println!("");
}
println!("Done");
}
This is the error I get
error[E0308]: mismatched types
--> src/main.rs:11:17
|
11 | for item in try!(fs::read_dir(dir)) {
| ^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
|
= note: expected type `()`
found type `std::result::Result<_, _>`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
I also tried the question mark operator:
for item in fs::read_dir(dir)? {
Which had a different error:
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> src/main.rs:11:17
|
11 | for item in fs::read_dir(dir)? {
| ^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
Previous versions of Rust had a similar error about std::ops::Carrier
Should I avoid try!() and ?? What is the best way to handle errors? Mostly I do it like this:
match error_prone {
Err(e) => {
println!("Error: {}", e);
return;
},
Ok(f) => f,
};
But if I have to use that in a for loop, it's a complete mess
for i in match error_prone {
// match code
} {
// loop code
}
try! is a macro that returns Errs automatically; ? is syntax that does mostly the same thing, but it works with any type that implements the Try trait.
As of Rust 1.22.0, Option implements Try, so it can be used with ?. Before that, ? could only be used in functions that return a Result. try! continues to only work with Results.
As of Rust 1.26.0, main is allowed to return a value that implements Termination. Before that, it doesn't return any value.
As of Rust 1.26.0
Your original code works if you mark main as returning a Result and then return Ok(()) in all the "success" cases:
use std::{fs, io, path::Path};
fn main() -> Result<(), io::Error> {
let dir = Path::new("../FileSystem");
if !dir.is_dir() {
println!("Is not a directory");
return Ok(());
}
for item in fs::read_dir(dir)? {
let file = match item {
Err(e) => {
println!("Error: {}", e);
return Ok(());
}
Ok(f) => f,
};
println!("");
}
println!("Done");
Ok(())
}
Before that
This is how you might transform your code to use ?:
use std::{error::Error, fs, path::Path};
fn print_dir_contents() -> Result<String, Box<Error>> {
let dir = Path::new("../FileSystem");
if !dir.is_dir() {
return Err(Box::from("Is not a directory!"));
}
for entry in fs::read_dir(dir)? {
let path = entry?.path();
let file_name = path.file_name().unwrap();
println!("{}", file_name.to_string_lossy());
}
Ok("Done".into())
}
fn main() {
match print_dir_contents() {
Ok(s) => println!("{}", s),
Err(e) => println!("Error: {}", e.to_string()),
}
}
There's a lot of error handling here that you might not expect - other languages don't tend to require it! But they exist in other languages - Rust just makes you know it. Here are the errors:
entry?
IO errors can happen during iteration.
path.file_name().unwrap()
Not all paths have file names. We can unwrap this because read_dir won't give us a path without a file name.
file_name.to_string_lossy()
You can also to_str and throw an error, but it's nicer to do this. This error exists because not all file names are valid Unicode.
try! and ? throw errors into the return value, converting them to Box::Error. It's actually more reasonable to return an amalgamated error of all the things that can go wrong. Luckily io::Error is just the right type:
use std::io;
// ...
fn print_dir_contents() -> Result<String, io::Error> {
// ...
if !dir.is_dir() {
return Err(io::Error::new(io::ErrorKind::Other, "Is not a directory!"));
}
// ...
}
Frankly, though, this check is already in fs::read_dir, so you can actually just remove the if !dis.is_dir altogether:
use std::{fs, io, path::Path};
fn print_dir_contents() -> Result<String, io::Error> {
let dir = Path::new("../FileSystem");
for entry in fs::read_dir(dir)? {
let path = entry?.path();
let file_name = path.file_name().unwrap();
println!("{}", file_name.to_string_lossy());
}
Ok("Done".into())
}
fn main() {
match print_dir_contents() {
Ok(s) => println!("{}", s),
Err(e) => println!("Error: {}", e.to_string()),
}
}
The ques_in_main RFC got merged recently. Once it's completed, the syntax in the question will indeed compile just fine and work as intended, provided the try!() calls are replaced with the ? operator.
As of Rust 1.26, Rust supports a return value from main(), and thus supports the use of the error-check operator ? (or equivalently the try!() macro) in main() when main() is defined to return a Result:
extern crate failure;
use failure::Error;
use std::fs::File;
type Result<T> = std::result::Result<T, Error>;
fn main() -> Result<()> {
let mut _file = File::open("foo.txt")?; // does not exist; returns error
println!("the file is open!");
Ok(())
}
The above compiles and returns a file not found error (assuming foo.txt does not exist in the local path).
Rust playground example
Veedrac's answer helped me too, although the OP's question is slightly different. While reading the Rust documentation, I saw this snippet:
use std::fs::File;
use std::io::prelude::*;
let mut file = File::open("foo.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
assert_eq!(contents, "Hello, world!");
Though in the Rust Book they point out the centrality of the main function, if you run this inside it you'll get a similar error. If you wrap the code inside a function handling the errors the aforementioned snippet works:
use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
fn print_file_content() -> Result<String, Box<Error>> {
let mut f = File::open("foo.txt")?;
let mut contents = String::new();
f.read_to_string(&mut contents)?;
println!("The content: {:?}", contents);
Ok("Done".into())
}
fn main() {
match print_file_content() {
Ok(s) => println!("{}", s),
Err(e) => println!("Error: {}", e.to_string()),
}
}
P.S. I'm learning Rust so these snippets are not intended as good Rust coding :)
Why does this code not compile?
use std::{fs, path::Path};
fn main() {
let dir = Path::new("../FileSystem");
if !dir.is_dir() {
println!("Is not a directory");
return;
}
for item in try!(fs::read_dir(dir)) {
let file = match item {
Err(e) => {
println!("Error: {}", e);
return;
}
Ok(f) => f,
};
println!("");
}
println!("Done");
}
This is the error I get
error[E0308]: mismatched types
--> src/main.rs:11:17
|
11 | for item in try!(fs::read_dir(dir)) {
| ^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
|
= note: expected type `()`
found type `std::result::Result<_, _>`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
I also tried the question mark operator:
for item in fs::read_dir(dir)? {
Which had a different error:
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> src/main.rs:11:17
|
11 | for item in fs::read_dir(dir)? {
| ^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
Previous versions of Rust had a similar error about std::ops::Carrier
Should I avoid try!() and ?? What is the best way to handle errors? Mostly I do it like this:
match error_prone {
Err(e) => {
println!("Error: {}", e);
return;
},
Ok(f) => f,
};
But if I have to use that in a for loop, it's a complete mess
for i in match error_prone {
// match code
} {
// loop code
}
try! is a macro that returns Errs automatically; ? is syntax that does mostly the same thing, but it works with any type that implements the Try trait.
As of Rust 1.22.0, Option implements Try, so it can be used with ?. Before that, ? could only be used in functions that return a Result. try! continues to only work with Results.
As of Rust 1.26.0, main is allowed to return a value that implements Termination. Before that, it doesn't return any value.
As of Rust 1.26.0
Your original code works if you mark main as returning a Result and then return Ok(()) in all the "success" cases:
use std::{fs, io, path::Path};
fn main() -> Result<(), io::Error> {
let dir = Path::new("../FileSystem");
if !dir.is_dir() {
println!("Is not a directory");
return Ok(());
}
for item in fs::read_dir(dir)? {
let file = match item {
Err(e) => {
println!("Error: {}", e);
return Ok(());
}
Ok(f) => f,
};
println!("");
}
println!("Done");
Ok(())
}
Before that
This is how you might transform your code to use ?:
use std::{error::Error, fs, path::Path};
fn print_dir_contents() -> Result<String, Box<Error>> {
let dir = Path::new("../FileSystem");
if !dir.is_dir() {
return Err(Box::from("Is not a directory!"));
}
for entry in fs::read_dir(dir)? {
let path = entry?.path();
let file_name = path.file_name().unwrap();
println!("{}", file_name.to_string_lossy());
}
Ok("Done".into())
}
fn main() {
match print_dir_contents() {
Ok(s) => println!("{}", s),
Err(e) => println!("Error: {}", e.to_string()),
}
}
There's a lot of error handling here that you might not expect - other languages don't tend to require it! But they exist in other languages - Rust just makes you know it. Here are the errors:
entry?
IO errors can happen during iteration.
path.file_name().unwrap()
Not all paths have file names. We can unwrap this because read_dir won't give us a path without a file name.
file_name.to_string_lossy()
You can also to_str and throw an error, but it's nicer to do this. This error exists because not all file names are valid Unicode.
try! and ? throw errors into the return value, converting them to Box::Error. It's actually more reasonable to return an amalgamated error of all the things that can go wrong. Luckily io::Error is just the right type:
use std::io;
// ...
fn print_dir_contents() -> Result<String, io::Error> {
// ...
if !dir.is_dir() {
return Err(io::Error::new(io::ErrorKind::Other, "Is not a directory!"));
}
// ...
}
Frankly, though, this check is already in fs::read_dir, so you can actually just remove the if !dis.is_dir altogether:
use std::{fs, io, path::Path};
fn print_dir_contents() -> Result<String, io::Error> {
let dir = Path::new("../FileSystem");
for entry in fs::read_dir(dir)? {
let path = entry?.path();
let file_name = path.file_name().unwrap();
println!("{}", file_name.to_string_lossy());
}
Ok("Done".into())
}
fn main() {
match print_dir_contents() {
Ok(s) => println!("{}", s),
Err(e) => println!("Error: {}", e.to_string()),
}
}
The ques_in_main RFC got merged recently. Once it's completed, the syntax in the question will indeed compile just fine and work as intended, provided the try!() calls are replaced with the ? operator.
As of Rust 1.26, Rust supports a return value from main(), and thus supports the use of the error-check operator ? (or equivalently the try!() macro) in main() when main() is defined to return a Result:
extern crate failure;
use failure::Error;
use std::fs::File;
type Result<T> = std::result::Result<T, Error>;
fn main() -> Result<()> {
let mut _file = File::open("foo.txt")?; // does not exist; returns error
println!("the file is open!");
Ok(())
}
The above compiles and returns a file not found error (assuming foo.txt does not exist in the local path).
Rust playground example
Veedrac's answer helped me too, although the OP's question is slightly different. While reading the Rust documentation, I saw this snippet:
use std::fs::File;
use std::io::prelude::*;
let mut file = File::open("foo.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
assert_eq!(contents, "Hello, world!");
Though in the Rust Book they point out the centrality of the main function, if you run this inside it you'll get a similar error. If you wrap the code inside a function handling the errors the aforementioned snippet works:
use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
fn print_file_content() -> Result<String, Box<Error>> {
let mut f = File::open("foo.txt")?;
let mut contents = String::new();
f.read_to_string(&mut contents)?;
println!("The content: {:?}", contents);
Ok("Done".into())
}
fn main() {
match print_file_content() {
Ok(s) => println!("{}", s),
Err(e) => println!("Error: {}", e.to_string()),
}
}
P.S. I'm learning Rust so these snippets are not intended as good Rust coding :)