Let's read the compiler error messages together!
error: no method named `path` found for type `std::fs::ReadDir` in the current scope
--> src/main.rs:5:26
|
5 | let path = entry.path();
| ^^^^
This means that the type of entry
is a ReadDir
, but how did that happen? We are supposed to be iterating over a ReadDir
!
If we look at the documentation for read_dir
, we can see that it returns a Result
:
pub fn read_dir<P: AsRef<Path>>(path: P) -> Result<ReadDir>
This means that the process of reading a directory can fail, which is entirely believable — what if the directory doesn't exist? However, the presented code doesn't handle that error. It instead passes the Result
to the for
loop. for
loops work by calling IntoIterator
, and Result
implements that, yielding the Ok
case or nothing at all. Option
has a similar implementation.
So, you added try!
to the code...
for entry in try!(fs::read_dir("/etc"))
error[E0308]: mismatched types
--> src/main.rs:4:18
|
4 | for entry in try!(fs::read_dir("/etc")) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
|
= note: expected type `()`
= note: found type `std::result::Result<_, _>`
= note: this error originates in a macro outside of the current crate
As has been discussed previously and is mentioned in the documentation for try!
:
Because of the early return, try!
can only be used in functions that return Result
.
You have to handle the error somehow, and for whatever reason, your current function states that it cannot fail — it doesn't return a Result
! Instead, let's just kill the entire program by panicking by adding expect
to the call:
for entry in fs::read_dir("/etc").expect("I told you this directory exists")
(Some people use unwrap
, but I will always advocate for expect
as it has a higher chance of providing useful information for the poor soul that experiences the eventual failure)
error: no method named `path` found for type `std::result::Result<std::fs::DirEntry, std::io::Error>` in the current scope
--> src/main.rs:5:26
|
5 | let path = entry.path();
| ^^^^
Yes, there are even more failure cases possible. Specifically, reading each entry may fail for some reason. That's why the ReadDir
iterator says
type Item = Result<DirEntry>
Again, your function still states it cannot fail, so we have to panic again:
let entry = entry.expect("I couldn't read something inside the directory");
error[E0277]: the trait bound `std::fs::DirEntry: std::fmt::Display` is not satisfied
--> src/main.rs:9:26
|
9 | print!("{}", entry);
| ^^^^^ trait `std::fs::DirEntry: std::fmt::Display` not satisfied
|
= note: `std::fs::DirEntry` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
= note: required by `std::fmt::Display::fmt`
As stated well in the error message, change {}
to {:?}
because DirEntry
has no proper way to be formatted for end-users. Programmers can deal with the debugging format.
use std::fs;
fn main() {
for entry in fs::read_dir("/etc").expect("I told you this directory exists") {
let entry = entry.expect("I couldn't read something inside the directory");
let path = entry.path();
if path.is_dir() {
print!("{:?}", entry);
}
}
}
I'd highly recommend re-reading the error handling chapter of The Rust Programming Language. I'd also advocate for basically memorizing the methods and traits implemented for Result
and Option
, seeing as how core they are to the Rust experience.
Here's a version that returns an error from main and uses the try operator (?
):
use std::{error::Error, fs};
fn main() -> Result<(), Box<dyn Error>> {
for entry in fs::read_dir("/etc")? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
print!("{:?}", entry);
}
}
Ok(())
}