Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rust From and Into traits
Rust 的 From 与 Into trait

What you’ll learn: Rust’s type conversion traits — From<T> and Into<T> for infallible conversions, TryFrom and TryInto for fallible ones. Implement From and get Into for free. Replaces C++ conversion operators and constructors.
本章将学到什么: Rust 的类型转换 trait,包括用于不会失败转换的 From<T>Into<T>,以及用于可能失败转换的 TryFromTryInto。只要实现了 FromInto 就会自动可用。这一套基本可以替代 C++ 里的转换运算符和部分构造器用途。

  • From and Into are complementary traits to facilitate type conversion
    FromInto 是一对互补的 trait,专门用来做类型转换。
  • Types normally implement on the From trait. the String::from() converts from “&str” to String, and compiler can automatically derive &str.into
    通常都是给类型实现 From。例如 String::from() 会把 &str 转成 String,而编译器也会自动让 &str.into() 成立。
struct Point {x: u32, y: u32}
// Construct a Point from a tuple
impl From<(u32, u32)> for Point {
    fn from(xy : (u32, u32)) -> Self {
        Point {x : xy.0, y: xy.1}       // Construct Point using the tuple elements
    }
}
fn main() {
    let s = String::from("Rust");
    let x = u32::from(true);
    let p = Point::from((40, 42));
    // let p : Point = (40.42)::into(); // Alternate form of the above
    println!("s: {s} x:{x} p.x:{} p.y {}", p.x, p.y);   
}

Exercise: From and Into
练习:From 与 Into

  • Implement a From trait for Point to convert into a type called TransposePoint. TransposePoint swaps the x and y elements of Point
    Point 实现一个 From trait,把它转换成一个叫 TransposePoint 的类型。TransposePoint 会把 Point 里的 xy 对调。
Solution 参考答案
struct Point { x: u32, y: u32 }
struct TransposePoint { x: u32, y: u32 }

impl From<Point> for TransposePoint {
    fn from(p: Point) -> Self {
        TransposePoint { x: p.y, y: p.x }
    }
}

fn main() {
    let p = Point { x: 10, y: 20 };
    let tp = TransposePoint::from(p);
    println!("Transposed: x={}, y={}", tp.x, tp.y);  // x=20, y=10

    // Using .into() — works automatically when From is implemented
    let p2 = Point { x: 3, y: 7 };
    let tp2: TransposePoint = p2.into();
    println!("Transposed: x={}, y={}", tp2.x, tp2.y);  // x=7, y=3
}
// Output:
// Transposed: x=20, y=10
// Transposed: x=7, y=3

Rust Default trait
Rust 的 Default trait

  • Default can be used to implement default values for a type
    Default 可以为类型提供默认值。
    • Types can use the Derive macro with Default or provide a custom implementation
      类型既可以直接派生 Default,也可以手写自定义实现。
#[derive(Default, Debug)]
struct Point {x: u32, y: u32}
#[derive(Debug)]
struct CustomPoint {x: u32, y: u32}
impl Default for CustomPoint {
    fn default() -> Self {
        CustomPoint {x: 42, y: 42}
    }
}
fn main() {
    let x = Point::default();   // Creates a Point{0, 0}
    println!("{x:?}");
    let y = CustomPoint::default();
    println!("{y:?}");
}

Rust Default trait
Default trait 的常见用法

  • Default trait has several use cases including
    Default trait 的常见用途包括:
    • Performing a partial copy and using default initialization for rest
      只覆盖部分字段,其余字段走默认初始化。
    • Default alternative for Option types in methods like unwrap_or_default()
      Option 一类类型提供默认回退值,例如 unwrap_or_default()
#[derive(Debug)]
struct CustomPoint {x: u32, y: u32}
impl Default for CustomPoint {
    fn default() -> Self {
        CustomPoint {x: 42, y: 42}
    }
}
fn main() {
    let x = CustomPoint::default();
    // Override y, but leave rest of elements as the default
    let y = CustomPoint {y: 43, ..CustomPoint::default()};
    println!("{x:?} {y:?}");
    let z : Option<CustomPoint> = None;
    // Try changing the unwrap_or_default() to unwrap()
    println!("{:?}", z.unwrap_or_default());
}

Other Rust type conversions
Rust 的其他类型转换方式

  • Rust doesn’t support implicit type conversions and as can be used for explicit conversions
    Rust 不支持隐式类型转换,需要显式转换时可以使用 as
  • as should be sparingly used because it’s subject to loss of data by narrowing and so forth. In general, it’s preferable to use into() or from() where possible
    as 要少用,因为它可能触发窄化转换,从而丢失数据。一般来说,能用 into()from() 就尽量用它们。
fn main() {
    let f = 42u8;
    // let g : u32 = f;    // Will not compile
    let g = f as u32;      // Ok, but not preferred. Subject to rules around narrowing
let g : u32 = f.into(); // Most preferred form; infallible and checked by the compiler
    //let k : u8 = f.into();  // Fails to compile; narrowing can result in loss of data
    
    // Attempting a narrowing operation requires use of try_into
    if let Ok(k) = TryInto::<u8>::try_into(g) {
        println!("{k}");
    }
}