ข้ามไปยังเนื้อหา

ตัวดำเนินการ ?

โดยไม่มีตัวดำเนินการ ? การส่งต่อข้อผิดพลาดจากฟังก์ชันที่เรียกต้องใช้นิพจน์ match ทุกครั้ง:

fn parse_and_double(s: &str) -> Result<i32, std::num::ParseIntError> {
let n = match s.parse::<i32>() {
Ok(v) => v,
Err(e) => return Err(e),
};
Ok(n * 2)
}

รูปแบบนี้ — แกะ Ok หรือคืน Err แบบ early — เกิดขึ้นบ่อยมากจน Rust มีตัวดำเนินการเฉพาะให้เลย

การวาง ? หลังนิพจน์ Result ทำสิ่งเดียวกันกับ match ด้านบน:

  • ถ้าค่าเป็น Ok(v) จะประเมินเป็น v
  • ถ้าค่าเป็น Err(e) จะคืน Err(e) ทันทีจากฟังก์ชันที่ครอบอยู่

ฟังก์ชันต้องคืน Result (หรือ Option) เพื่อให้ ? ทำงาน — คอมไพเลอร์บังคับใช้สิ่งนี้

use std::num::ParseIntError;
fn parse_and_double(s: &str) -> Result<i32, ParseIntError> {
let n: i32 = s.parse()?; // ? returns early on Err
Ok(n * 2)
}

เพราะ ? คืนค่า Ok โดยตรง คุณสามารถเรียกฟังก์ชันที่ล้มเหลวได้หลายตัวตามลำดับโดยไม่ต้องซ้อนกัน:

use std::num::ParseIntError;
fn parse_and_double(s: &str) -> Result<i32, ParseIntError> {
let n: i32 = s.parse()?;
Ok(n * 2)
}
fn chain(a: &str, b: &str) -> Result<i32, ParseIntError> {
let x = parse_and_double(a)?;
let y = parse_and_double(b)?;
Ok(x + y)
}

? แต่ละตัวจะแกะค่าสำเร็จหรือ short-circuit ฟังก์ชันทั้งหมด ไม่มีการ match ซ้อนกัน ไม่มีการ return แบบ manual

main ยังสามารถคืน Result<(), Box<dyn std::error::Error>> ซึ่งให้คุณใช้ ? ภายใน main ได้:

fn main() -> Result<(), Box<dyn std::error::Error>> {
let n: i32 = "42".parse()?;
println!("parsed: {}", n);
Ok(())
}

Box<dyn std::error::Error> คือ trait object ที่สามารถเก็บประเภทข้อผิดพลาดใดก็ได้ ทำให้มีประโยชน์ใน main ที่คุณไม่ต้องการแก้ไขประเภทข้อผิดพลาดเดียวที่เป็น concrete

เมื่อประเภทข้อผิดพลาดที่คืนโดยฟังก์ชันที่เรียกแตกต่างจากประเภทข้อผิดพลาดของฟังก์ชันที่ครอบอยู่ ? จะเรียก From::from โดยอัตโนมัติเพื่อแปลง นี่คือวิธีที่ฟังก์ชันที่คืน MyError สามารถใช้ ? บนการเรียกที่คืน ParseIntError ได้ ตราบใดที่ From<ParseIntError> for MyError ถูก implement ไว้

use std::num::ParseIntError;
fn parse_and_double(s: &str) -> Result<i32, ParseIntError> {
let n: i32 = s.parse()?; // ? returns early on Err
Ok(n * 2)
}
fn chain(a: &str, b: &str) -> Result<i32, ParseIntError> {
let x = parse_and_double(a)?;
let y = parse_and_double(b)?;
Ok(x + y)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let doubled = parse_and_double("21")?;
println!("doubled: {}", doubled);
let sum = chain("10", "5")?;
println!("chain(10, 5): {}", sum);
// Demonstrate early return on error
match parse_and_double("abc") {
Ok(v) => println!("ok: {}", v),
Err(e) => println!("early return caught: {}", e),
}
println!("main returned Ok(())");
Ok(())
}
? ทำอะไรเมื่อนำไปใช้กับค่า Err(e)?
ฟังก์ชันที่ครอบอยู่ต้องตรงตามข้อจำกัดใดเพื่อให้ ? คอมไพล์ได้?
ประเภทใดที่มักใช้ใน main() -> Result<(), ...> เพื่อรับข้อผิดพลาดใดก็ได้?
? จัดการกับประเภทข้อผิดพลาดที่ไม่ตรงกันระหว่าง caller และ callee อย่างไร?