Rust From and Into traits
Rust 的 From 与 Into trait
What you’ll learn: Rust’s type conversion traits —
From<T>andInto<T>for infallible conversions,TryFromandTryIntofor fallible ones. ImplementFromand getIntofor free. Replaces C++ conversion operators and constructors.
本章将学到什么: Rust 的类型转换 trait,包括用于不会失败转换的From<T>和Into<T>,以及用于可能失败转换的TryFrom和TryInto。只要实现了From,Into就会自动可用。这一套基本可以替代 C++ 里的转换运算符和部分构造器用途。
FromandIntoare complementary traits to facilitate type conversionFrom和Into是一对互补的 trait,专门用来做类型转换。- Types normally implement on the
Fromtrait. theString::from()converts from “&str” toString, 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
Fromtrait forPointto convert into a type calledTransposePoint.TransposePointswaps thexandyelements ofPoint
为Point实现一个Fromtrait,把它转换成一个叫TransposePoint的类型。TransposePoint会把Point里的x和y对调。
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
Defaultcan be used to implement default values for a typeDefault可以为类型提供默认值。- Types can use the
Derivemacro withDefaultor provide a custom implementation
类型既可以直接派生Default,也可以手写自定义实现。
- Types can use the
#[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 的常见用法
Defaulttrait has several use cases includingDefaulttrait 的常见用途包括:- Performing a partial copy and using default initialization for rest
只覆盖部分字段,其余字段走默认初始化。 - Default alternative for
Optiontypes in methods likeunwrap_or_default()
给Option一类类型提供默认回退值,例如unwrap_or_default()。
- Performing a partial copy and using default initialization for rest
#[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
ascan be used forexplicitconversions
Rust 不支持隐式类型转换,需要显式转换时可以使用as。 asshould be sparingly used because it’s subject to loss of data by narrowing and so forth. In general, it’s preferable to useinto()orfrom()where possibleas要少用,因为它可能触发窄化转换,从而丢失数据。一般来说,能用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}");
}
}