Why do try!() and ? not compile when used in a function that doesn't return Option or Result?

后端 未结 4 1191
礼貌的吻别
礼貌的吻别 2020-11-22 06:18

Why does this code not compile?

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

fn main() {
    let dir = Path::new(\"../FileSystem\");

    if !dir.is_dir() {
        println!(         


        
相关标签:
4条回答
  • 2020-11-22 06:47

    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 :)

    0 讨论(0)
  • 2020-11-22 06:52

    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

    0 讨论(0)
  • 2020-11-22 07:01

    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()),
        }
    }
    
    0 讨论(0)
  • 2020-11-22 07:07

    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.

    0 讨论(0)
提交回复
热议问题