1

I've written a pretty straight-forward script based on the Rust docs:

use std::fs::{self, DirEntry};
use std::path::Path;

fn main() {
    let path = Path::new(".");
    for entry in fs::read_dir(path)? {
        let entry = entry?;
        let path = entry.path();
        if path.is_dir() {
            println!("directory found!");
        }
    }

}

but I get the following compile errors about ?:

error[E0277]: the trait bound `(): std::ops::Carrier` is not satisfied
 --> test.rs:6:18
  |
6 |     for entry in fs::read_dir(path)? {
  |                  -------------------
  |                  |
  |                  the trait `std::ops::Carrier` is not implemented for `()`
  |                  in this macro invocation
  |
  = note: required by `std::ops::Carrier::from_error`

error[E0277]: the trait bound `(): std::ops::Carrier` is not satisfied
 --> test.rs:7:21
  |
7 |         let entry = entry?;
  |                     ------
  |                     |
  |                     the trait `std::ops::Carrier` is not implemented for `()`
  |                     in this macro invocation
  |
  = note: required by `std::ops::Carrier::from_error`

I only partially understand ? but I know the gist is that it allows you to act on a Result only if it's an Ok. The error here is that it's being used on a () rather than a Result, which is weird. I tried implementing the loop without ?:

use std::fs::{self, DirEntry};
use std::path::Path;

fn main() {
    let path = Path::new(".");
    for entry in fs::read_dir(path) {
        println!("{}", entry.path());
    }

}

But I get the error:

error: no method named `path` found for type `std::fs::ReadDir` in the current scope
 --> test.rs:7:30
  |
7 |         println!("{}", entry.path());
  |                              ^^^^

Which implies that instead of fs::read_dir returning ReadDir which is an iterator over DirEntry items, fs::read_dir is returning () which is somehow an iterator over ReadDir items?

I'm so confused.

It's probably worth mentioning that i'm running: rustc 1.16.0 (30cf806ef 2017-03-10)

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
eiko
  • 5,110
  • 6
  • 17
  • 35
  • You may find this not having to do with accessing files in a directory at all: in the docs, that code was not inside `main()`. – E_net4 Apr 13 '17 at 14:54

2 Answers2

9

The first error is because you cannot use try! or ? in a function returning ().

The second error is because read_dir returns a Result:

pub fn read_dir<P: AsRef<Path>>(path: P) -> Result<ReadDir>

Result implements IntoIterator, so path is actually the iterator you think you had.

Handling the errors and calling Path::display gets you what you want:

use std::fs;
use std::path::Path;

fn main() {
    let path = Path::new(".");
    for entry in fs::read_dir(path).expect("Unable to list") {
        let entry = entry.expect("unable to get entry");
        println!("{}", entry.path().display());
    }
}
Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • thank you! this answers the question that lead me to say i only partially understood `?` (that question being "what does `?` do when the value *isn't* `Ok`?). is there a resource i missed? `?` and `try!` wasn't mentioned in the rust book, but the docs seem to assume common knowledge of it. – eiko Apr 13 '17 at 15:05
  • 1
    @eiko http://rustbyexample.com/std/result/try.html. The ? operator is just sugar around try! so it should throw a compilation error about that type not implementing the carrier trait if it is not Result. – asteriskTheServer Apr 13 '17 at 15:11
  • 2
    @eiko the main resource I'd suggest is the [error handling chapter](https://doc.rust-lang.org/stable/book/error-handling.html) of the book. `try!` is the previous idiomatic way for propagating errors, but `?` has replaced it in newer versions of Rust. `try!` still exists for backwards-compatibility reasons. – Shepmaster Apr 13 '17 at 15:12
  • 3
    @asteriskTheServer `?` is *not* sugar around `try!`. If it was; you'd get the same error from using either, which is not currently the case. If anything, the `try!` macro should delegate to `?`. – Shepmaster Apr 13 '17 at 15:14
  • oops, i guess `try!` was in the book after all. i may have been skimming by the time i got to chapter 5.7 ^-^' thank you all for your help! – eiko Apr 13 '17 at 15:15
  • @Shepmaster oh? I read a couple times that it was sugar around try!. For example, this blog presents it as such https://m4rw3r.github.io/rust-questionmark-operator and I know a couple of Reddit posts have presented it as such. I'll actually read the RFC to get a better understanding the ? operator. Thanks for pointing that out. :) – asteriskTheServer Apr 13 '17 at 15:28
  • @asteriskTheServer there's also [The One True Source](https://github.com/rust-lang/rust/blob/43ef63d5b4279ef97ed921338a69b0d62101ca83/src/libcore/macros.rs#L329-L336) (and just trying both in an example). To be clear - you should be able to *use* them interchangeably, and I don't know if the difference is temporary or permanent. – Shepmaster Apr 13 '17 at 15:42
5

The ? operator and the try! macro will only work when your function returns a Result (in which the raised errors can be properly converted, of course). The main function does not return a result.

You may wish to send all of your code to a separate function and handle the error in main(), with something like this:

use std::io;
use std::fs::{self, DirEntry};
use std::path::Path;

fn main() {
    run().unwrap_or_else(|e| {
      println!("Something went wrong: {}", e.to_string());
    });
}

fn run() -> io::Result<()> {
    let path = Path::new(".");
    for entry in fs::read_dir(path)? {
        let entry = entry?;
        let path = entry.path();
        if path.is_dir() {
            println!("directory found!");
        }
    }
    Ok(())
}
E_net4
  • 27,810
  • 13
  • 101
  • 139