Asked  7 Months ago    Answers:  5   Viewed   29 times

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
}

 Answers

85

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()),
    }
}
Tuesday, June 1, 2021
 
binoculars
answered 7 Months ago
36

It is a quirk of the syntax for passing arrays to functions.

Actually it is not possible to pass an array in C. If you write syntax that looks like it should pass the array, what actually happens is that a pointer to the first element of the array is passed instead.

Since the pointer does not include any length information, the contents of your [] in the function formal parameter list are actually ignored.

The decision to allow this syntax was made in the 1970s and has caused much confusion ever since...

Tuesday, June 1, 2021
 
linjuming
answered 7 Months ago
39
Tok:tmp doug$ du -a

8   ./Cargo.toml
8   ./src/bin.rs
8   ./src/lib.rs
16  ./src

Cargo.toml:

[package]
name = "mything"
version = "0.0.1"
authors = ["me <me@gmail.com>"]

[lib]
name = "mylib"
path = "src/lib.rs"

[[bin]]
name = "mybin"
path = "src/bin.rs"

src/lib.rs:

pub fn test() {
    println!("Test");
}

src/bin.rs:

extern crate mylib; // not needed since Rust edition 2018

use mylib::test;

pub fn main() {
    test();
}
Sunday, June 6, 2021
 
PHPWDev
answered 6 Months ago
50

It depends: do you wish to return a handle or a copy.

If you wish to return a handle:

  • Person*
  • boost::optional<Person&>

are both acceptable choices. I tend to use a Ptr<Person> class which throws in case of null access, but that's my paranoia.

If you wish to return a copy:

  • boost::optional<Person> for non polymorphic classes
  • std::unique_ptr<Person> for polymorphic classes

because dynamic allocation incurs an overhead, so you only use it when necessary.

Wednesday, July 28, 2021
 
Gregosaurus
answered 5 Months ago
31

I've succeeded to run the non-optimised code without abnormalities. Thanks to Notlikethat for the idea. My stack was just mapped into readonly memory.

So I've just added the offset statement into my linker script (". = 1024M;") in order to make all the symbols to start from 1GiB (where RAM begins). After this modification the code started to work properly.

Monday, August 23, 2021
 
cletus
answered 4 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share