1

Тема: Помилка при використанні match, несумісні типи

Намагаюся прочитати файл і перехопити помилку, якщо файл не прочитається:

let mut file = match File::open(&file_path) {
        Err(why) =>{
            println!("{}", why.to_string());  
            }, 
        Ok(file) => file,
    };

Але отримую ось таку помилку для match:

error[E0308]: `match` arms have incompatible types
   |
21 |     let mut file = match File::open(&file_path) {
   |                    ---------------------------- `match` arms have incompatible types
22 |         Err(why) =>{
23 |             println!("{}", why.to_string());
   |             ------------------------------- this is found to be of type `()`
...
31 |         Ok(file) => file,
   |                     ^^^^ expected `()`, found `File`

Не розумію, як це виправити.

2

Re: Помилка при використанні match, несумісні типи

А що саме має бути присвоєне змінній file у випадку, коли File::open поверне Err? Очевидно, що файлу тоді немає. Відповідно, якщо ви хочете робити це таким чином - то робіть

match File::open(&file_path) {
    Err(why) =>{
        // тут немає ніякого файла
        println!("{}", why.to_string());  
    }, 
    Ok(file) => {
        /* ось тут пишіть код, що працює зі змінною file */
    }
};

але майже напевно вам краще повертати з функції Result і використовувати оператор ?. Якщо це не бібліотека і не критичний за часом код (операції введення-виведення очевидно некритичні), можете використати anyhow для cпрощення обробки помилки.

Подякували: Teg Miles, leofun012

3

Re: Помилка при використанні match, несумісні типи

Ще варіант - додати в першу гілку return.

4

Re: Помилка при використанні match, несумісні типи

koala написав:

Ще варіант - додати в першу гілку return.

Я там panic!() додав замість return, щоб повністю зупиняти програму
після виведення вікна з описом помилки.

5

Re: Помилка при використанні match, несумісні типи

Якщо вам треба панікувати, то саме це роблять unwrap() і expect().

let mut file = File::open(&file_path).unwrap();
Подякували: leofun011

6

Re: Помилка при використанні match, несумісні типи

koala написав:

Якщо вам треба панікувати, то саме це роблять unwrap() і expect().

let mut file = File::open(&file_path).unwrap();

Мені потрібно перехопити помилку, вивести її опис в окремому вікні для користувача і закрити програму після цього.

7

Re: Помилка при використанні match, несумісні типи

Teg Miles написав:
koala написав:

Якщо вам треба панікувати, то саме це роблять unwrap() і expect().

let mut file = File::open(&file_path).unwrap();

Мені потрібно перехопити помилку, вивести її опис в окремому вікні для користувача і закрити програму після цього.

let mut file = File::open(&file_path).inspect_err( |why| println!("{why:?}"))
                                     .unwrap();
Подякували: leofun01, Teg Miles2

8

Re: Помилка при використанні match, несумісні типи

Але ще раз, краще повертати з функцій, що можуть завершитися невдачею, Result за допомогою ?, і обробляти всі помилки в одному місці.
Зокрема, panic! не завершить програму, в якої є інші потоки. Зробите застосунок багатопотоковим - доведеться потім всі обробники переписувати.

Подякували: leofun011

9

Re: Помилка при використанні match, несумісні типи

koala написав:

Але ще раз, краще повертати з функцій, що можуть завершитися невдачею, Result за допомогою ?, і обробляти всі помилки в одному місці.
Зокрема, panic! не завершить програму, в якої є інші потоки. Зробите застосунок багатопотоковим - доведеться потім всі обробники переписувати.

Так, я вже це зрозумів. Просто міркував стандартами С++ щодо обробки помилок. В Rust набагато зручніший підхід.
Але доведеться переписати всі функції, які щось повертають, через Result або Option.
Ось що зараз зробив:

    let file_path = Path::new(&file_path_string);
    let display = file_path.display();
    let mut file:File;
    match File::open(&file_path) {
        Err(why) =>{
            let error_message = format!("Couldn't open a file {}: {}", display, why);
            return Err(error_message.into());
            },
        Ok(new_file) =>{file = new_file;},
    };

Використовую match, а не «?», бо формую свій текст помилки для виводу.
А тоді обробляю ось так:

.inspect_err(|err| println!("{err:#?}")).unwrap();

Там замість println! буде діалогове вікно для показу помилки користувачу.

10

Re: Помилка при використанні match, несумісні типи

Ви весь час намагаєтеся написати велосипед. anyhow і thiserror не дивилися?

Подякували: leofun011

11

Re: Помилка при використанні match, несумісні типи

koala написав:

Ви весь час намагаєтеся написати велосипед. anyhow і thiserror не дивилися?

Не знав про них, але не хочу поки що додавати нові залежності.

12

Re: Помилка при використанні match, несумісні типи

Це фактично стандарт, як і regex та itertools. Звісно, можна без них, але нащо?

Подякували: leofun011

13

Re: Помилка при використанні match, несумісні типи

Ще тут може згодитися let else. Якось я пропустив, а воно ж з версії 1.65 (листопад 22 року) є. Мабуть, саме тому, що 22 рік, я взагалі не стежив тоді.

Стандартний спосіб "розгортати" змінні - зіставлення з шаблоном у if let:

if let Some(x) = option {
     тут є змінна x
} else {
    інша ситуація
}

Ну так от, якщо "інша ситуація" є розбіжною (diverge), тобто завжди або панікує, або робить return (що досить часто відбувається), це можна скоротити до

let Some(x) = option else {
    інша ситуація
}
тут є змінна x

Хоча тоді саму помилку не захопите.

Подякували: leofun011

14

Re: Помилка при використанні match, несумісні типи

koala написав:

Ще тут може згодитися let else. Якось я пропустив, а воно ж з версії 1.65 (листопад 22 року) є. Мабуть, саме тому, що 22 рік, я взагалі не стежив тоді.

Стандартний спосіб "розгортати" змінні - зіставлення з шаблоном у if let:

if let Some(x) = option {
     тут є змінна x
} else {
    інша ситуація
}

Ну так от, якщо "інша ситуація" є розбіжною (diverge), тобто завжди або панікує, або робить return (що досить часто відбувається), це можна скоротити до

let Some(x) = option else {
    інша ситуація
}
тут є змінна x

Хоча тоді саму помилку не захопите.

if let — гарний вибір, якщо треба якийсь один варіант відсіяти.
Ось чим у мене все закінчилося.

fn get_fluent_bundle(new_settings: &gio::Settings,
    new_window: Rc<gtk::ApplicationWindow>) ->FluentBundle<FluentResource>{
    let new_fluent_bundle: FluentBundle<FluentResource>;
    match download_fluent_bundle(&new_settings){
        Err(why) => {
            let error_message = format!(
                "{}\n Therefore default en-US locale was downloaded.", why.to_string());
            get_error_dialog(&error_message, new_window);
            let langid: LanguageIdentifier = "en-US".parse().expect("LangId parsing failed");
            new_fluent_bundle = FluentBundle::new(vec![langid]);
            },
        Ok(bundle) => {new_fluent_bundle = bundle;},
    };
    new_fluent_bundle
}

FluenBundle мені в будь-якому разі треба повертати, бо він далі використовується для локалізації інтерфейсу.
Просто у випадку помилки він порожній плюс повідомлення про помилку користувачу.
Ось як переклад слова завантажується:

let quit_title = get_translated_word(&self.fluent_bundle, &"Quit".to_string())
            .unwrap_or("Quit".to_string());

unwrap_or створює стандартну англійську версію інтерфейсу,
що працює за будь-яких умов, бо вона, по суті, вбудована в код.

15 Востаннє редагувалося koala (20.04.2024 10:24:29)

Re: Помилка при використанні match, несумісні типи

А, тобто вам треба розгорнути і так, і так? Я думав, воно має при помилці панікувати. Ну то

fn get_fluent_bundle(new_settings: &gio::Settings,
    new_window: Rc<gtk::ApplicationWindow>) ->FluentBundle<FluentResource> {
    download_fluent_bundle(&new_settings)
        .unwrap_or_else(|why| {
            let error_message = format!("{why}\n Therefore default en-US locale was downloaded.");
              //я не знаю тип Error, але 95%, що він impl Display, якщо impl ToString
            get_error_dialog(&error_message, new_window);
            let langid: LanguageIdentifier = "en-US".parse().expect("LangId parsing failed");
            FluentBundle::new(vec![langid])
       })
}
Подякували: Teg Miles1

16

Re: Помилка при використанні match, несумісні типи

koala написав:

А, тобто вам треба розгорнути і так, і так? Я думав, воно має при помилці панікувати. Ну то

fn get_fluent_bundle(new_settings: &gio::Settings,
    new_window: Rc<gtk::ApplicationWindow>) ->FluentBundle<FluentResource> {
    download_fluent_bundle(&new_settings)
        .unwrap_or_else(|why| {
            let error_message = format!("{why}\n Therefore default en-US locale was downloaded.");
              //я не знаю тип Error, але 95%, що він impl Display, якщо impl ToString
            get_error_dialog(&error_message, new_window);
            let langid: LanguageIdentifier = "en-US".parse().expect("LangId parsing failed");
            FluentBundle::new(vec![langid])
       })
}

Я теж так спочатку думав, але згодом зрозумів, що це некритична помилка, яку можна виправити.
Там Display я створюю для помилки в іншій функції, коли файл з перекладами читається.

    let file_path = Path::new(&file_path_string);
    let display = file_path.display();

    let mut file:File;
    match File::open(&file_path) {
        Err(why) =>{
            let error_message = format!("Couldn't open a file {}: {}", display, why);
            return Err(error_message.into());
            },
        Ok(new_file) =>{file = new_file;},
    };

17

Re: Помилка при використанні match, несумісні типи

Ну тобто тепер ви ще й Result<..., String> повертаєте? Це антипатерн.

18

Re: Помилка при використанні match, несумісні типи

koala написав:

Ну тобто тепер ви ще й Result<..., String> повертаєте? Це антипатерн.

Повертається ось так Result<FluentBundle<FluentResource>, Box<dyn Error>>.
Я перетворюю String в Error ось цим return Err(error_message.into()).

19

Re: Помилка при використанні match, несумісні типи

Тобто ви отримуєте якийсь Error, викликаєте у нього to_string і загортаєте результат у новий Error? А чому б тоді просто не передавати той Error, що ви отримали, без зайвих перетворень?

Подякували: leofun011

20

Re: Помилка при використанні match, несумісні типи

koala написав:

Тобто ви отримуєте якийсь Error, викликаєте у нього to_string і загортаєте результат у новий Error? А чому б тоді просто не передавати той Error, що ви отримали, без зайвих перетворень?

Мені потрібно повідомити користувачу з яким саме файлом виникли проблеми.
Тому я додаю шлях до проблемного файлу до звичайного Error,
загортаю в новий Error та передаю далі, де я виведу все це через окреме діалогове вікно,
щоб показати користувачу.