Reputation: 191819
I am trying to list all of the directories (not recursively) in a directory using Rust. I am mostly following the example code from the read_dir
docs, but I am trying to make it simpler since I know what the directory is ahead of time and I don't need to recurse.
I have it down to:
for entry in read_dir(Path::new("known-directory")) {
let path = entry.path();
if path.is_dir() {
print!("{}", entry);
}
}
This doesn't work since I get complaints of
no method named `path` found for type `std::fs::ReadDir` in the current scope
It seems like for
is not actually iterating over the entries of ReadDir
.
I have also tried try!(read_dir(Path::new("DefinitelyTyped")))
similar to what is in the docs, but this yields
expected (), found enum `std::result::Result`
Using let entry = try!(entry)
does not work either.
Ultimately I would like to push these directory entries to an array, sort it, and JSON-stringify it but of course first I have to be able iterate through the entries properly. How can I do this?
Version: rustc 1.13.0
Running with: cargo 0.13.0
Upvotes: 2
Views: 2215
Reputation: 432149
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 returnResult
.
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(())
}
Upvotes: 14