পাঠ ৯.২

Result দিয়ে recoverable error

Recoverable Errors with Result

বেশিরভাগ error program-কে পুরোপুরি crash করার মতো না — file না পাওয়া, permission denied, network down — এদের handle করে এগিয়ে যাওয়া যায়। Result<T, E> এই কাজ করে।

Result<T, E>

enum Result<T, E> {
    Ok(T),
    Err(E),
}
  • T = success value-এর type।
  • E = error value-এর type।

File::open — match দিয়ে handle

use std::fs::File;

fn main() {
    let greeting_file_result = File::open("hello.txt");

    let greeting_file = match greeting_file_result {
        Ok(file) => file,
        Err(error) => panic!("Problem opening the file: {error:?}"),
    };
}

File::open Result<File, io::Error> return করে। Match-এ Ok হলে file নিচ্ছি, Err হলে panic।

আলাদা error-এ আলাদা response

File না থাকলে create করো, অন্য error হলে panic — nested match:

use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let greeting_file_result = File::open("hello.txt");

    let greeting_file = match greeting_file_result {
        Ok(file) => file,
        Err(error) => match error.kind() {
            ErrorKind::NotFound => match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => panic!("Problem creating the file: {e:?}"),
            },
            _ => {
                panic!("Problem opening the file: {error:?}");
            }
        },
    };
}

Verbose। Closure-based alternative — Chapter 13-এ closure বিস্তারিত, আপাতত preview:

use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let greeting_file = File::open("hello.txt").unwrap_or_else(|error| {
        if error.kind() == ErrorKind::NotFound {
            File::create("hello.txt").unwrap_or_else(|error| {
                panic!("Problem creating the file: {error:?}");
            })
        } else {
            panic!("Problem opening the file: {error:?}");
        }
    });
}

unwrap এবং expect — shortcut

.unwrap() — Ok হলে value, Err হলে panic (default message সহ):

use std::fs::File;

fn main() {
    let greeting_file = File::open("hello.txt").unwrap();
}

.expect("...") — same behavior, কিন্তু custom panic message:

use std::fs::File;

fn main() {
    let greeting_file = File::open("hello.txt")
        .expect("hello.txt should be included in this project");
}

Production code-এ expect preferred — message-এ context থাকে কেন এই error সম্ভব না।

Error propagate করা

Error caller-এর কাছে পাঠাও — caller decide করুক কী করবে। Manual version:

use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
    let username_file_result = File::open("hello.txt");

    let mut username_file = match username_file_result {
        Ok(file) => file,
        Err(e) => return Err(e),
    };

    let mut username = String::new();

    match username_file.read_to_string(&mut username) {
        Ok(_) => Ok(username),
        Err(e) => Err(e),
    }
}

? operator — propagation-এর shortcut

use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
    let mut username_file = File::open("hello.txt")?;
    let mut username = String::new();
    username_file.read_to_string(&mut username)?;
    Ok(username)
}

? behavior:

  • Ok হলে — value extract করে statement-এ বসায়।
  • Err হলে — function থেকে immediate return Err(...)

? match-এর চেয়ে কিছু বেশি করে — error type-এ From::from apply করে। তাই এক type-এর error অন্য type-এ automatic convert হয় (যদি From implement করা থাকে)।

Method chain সহ ?

use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
    let mut username = String::new();

    File::open("hello.txt")?.read_to_string(&mut username)?;

    Ok(username)
}

আরো ছোট — fs::read_to_string:

use std::fs;
use std::io;

fn read_username_from_file() -> Result<String, io::Error> {
    fs::read_to_string("hello.txt")
}

? কোথায় use করা যায়

? use করতে হলে enclosing function-এর return type compatible হতে হবে — সাধারণত:

  • Result<T, E> return করে।
  • Option<T> return করে।
  • FromResidual implement-করা type return করে।

না হলে compile error:

use std::fs::File;

fn main() {
    let greeting_file = File::open("hello.txt")?;
}
compile errortext
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)

? Option-এ

fn last_char_of_first_line(text: &str) -> Option<char> {
    text.lines().next()?.chars().last()
}

fn main() {
    assert_eq!(
        last_char_of_first_line("Hello, world\nHow are you today?"),
        Some('d')
    );

    assert_eq!(last_char_of_first_line(""), None);
    assert_eq!(last_char_of_first_line("\nhi"), None);
}

Option-এ ?None হলে immediate return None; Some হলে value extract।

Result আর Option ? দিয়ে directly mix করা যায় না — .ok() বা .ok_or(...) দিয়ে explicit convert করতে হয়।

main()-এ Result return

use std::error::Error;
use std::fs::File;

fn main() -> Result<(), Box<dyn Error>> {
    let greeting_file = File::open("hello.txt")?;

    Ok(())
}
  • Box<dyn Error> — যেকোনো Error type-এর trait object।
  • Ok(()) — exit code 0; Err(...) — non-zero (C-convention)।
  • main-এ ? use করতে এই signature দরকার।

এই পাঠ থেকে যা শিখলে

  • Result<T, E> = Ok(T) বা Err(E)।
  • Match দিয়ে handle, error.kind() দিয়ে specific case।
  • .unwrap() default panic; .expect("...") custom message। Production-এ expect preferred।
  • ? = Ok value বা Err early-return; auto From conversion।
  • ? Result-returning function-এ; Option-এও কাজ করে; main-এ চাইলে fn main() -> Result<(), Box<dyn Error>>