Подтвердить что ты не робот

Зачем пытаться!() И? не компилироваться при использовании в функции, которая не возвращает параметр или результат?

Почему этот код не компилируется?

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");
}

Это ошибка, которую я получаю

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)

Я также попробовал оператор вопросительного знака:

for item in fs::read_dir(dir)? {

Который имел другую ошибку:

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'

Предыдущие версии Rust имели аналогичную ошибку в отношении std::ops::Carrier

Должен ли я избегать try!() И ? ? Каков наилучший способ обработки ошибок? В основном я делаю это так:

match error_prone {
    Err(e) => {
        println!("Error: {}", e);
        return;
    },
    Ok(f) => f,
};

Но если я должен использовать это в цикле for, это полный беспорядок

for i in match error_prone {
    // match code
} {
    // loop code
}
4b9b3361

Ответ 1

try! - макрос, который автоматически возвращает Err; ? это синтаксис, который делает в основном одно и то же, но он работает с любым типом, который реализует свойство Try.

Начиная с Rust 1.22.0, Option реализует Try, поэтому его можно использовать ? , До этого ? может использоваться только в функциях, возвращающих Result. try! продолжает работать только с Result s.

Начиная с Rust 1.26.0, main разрешено возвращать значение, которое реализует Termination. До этого он не возвращает никакой ценности.

Начиная с Rust 1.26.0

Ваш исходный код работает, если вы помечаете main как возвращающий Result и затем возвращаете Ok(()) во всех случаях "успеха":

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(())
}

До этого

Вот как вы можете преобразовать свой код для использования ? :

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()),
    }
}

Там много ошибок обработки здесь, что вы не можете ожидать - другие языки не склонны требовать этого! Но они существуют на других языках - Rust просто заставляет вас это знать. Вот ошибки:

entry?

Ошибки ввода-вывода могут возникать во время итерации.

path.file_name().unwrap()

Не все пути имеют имена файлов. Мы можем unwrap это, потому что read_dir не даст нам путь без имени файла.

file_name.to_string_lossy()

Вы также можете to_str и выбросить ошибку, но лучше сделать это. Эта ошибка существует, потому что не все имена файлов действительны для Unicode.

try! и ? введите ошибки в возвращаемое значение, преобразуя их в Box::Error. На самом деле разумнее вернуть объединенную ошибку всех вещей, которые могут пойти не так. К счастью io::Error - это правильный тип:

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!"));
    }

    // ...
}

Честно говоря, эта проверка уже находится в fs::read_dir, поэтому вы можете просто удалить if !dis.is_dir:

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()),
    }
}

Ответ 2

Недавно ques_in_main RFC недавно слился. Как только он будет завершен, синтаксис в вопросе будет действительно компилироваться просто отлично и работать по назначению, если вызовы try!() Заменяются на ? оператор.

Ответ 3

Начиная с Rust 1.26, Rust поддерживает возвращаемое значение из main() и, таким образом, поддерживает использование оператора проверки ошибок ? (или эквивалентно макросу try!()) в main() когда main() определен для возврата 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(())
}

Вышеприведенное компилирует и возвращает ошибку, не найденную в файле (предполагается, что foo.txt не существует в локальном пути).

Пример площадки ржавчины

Ответ 4

Ответ Veedrac тоже помог мне, хотя вопрос OP немного отличается. При чтении документации Rust я увидел этот фрагмент:

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!");

Хотя в книге Rust они указывают на центральность основной функции, если вы запустите ее внутри, вы получите аналогичную ошибку. Если вы оберните код внутри функции, обрабатывающей ошибки, описанные выше:

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. Я изучаю Rust, поэтому эти фрагменты не предназначены для хорошего кодирования Rust:)