newtype and Sized
Newtype
孤儿规则要求:只有当 trait 或类型其一属于当前 crate 时,才能为类型实现该 trait。
newtype 模式 通过定义一个 元组结构体 新类型,帮助绕开这一限制。
- 🌟
use std::fmt; /* 定义 Wrapper 类型 */ __; // Display 是外部 trait impl fmt::Display for Wrapper { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[{}]", self.0.join(", ")) } } fn main() { // Vec 是外部类型,不能直接为 Vec 实现外部 trait Display let w = Wrapper(vec![String::from("hello"), String::from("world")]); println!("w = {}", w); }
- 🌟 隐藏原类型的方法
/* 让代码通过编译 */ struct Meters(u32); fn main() { let i: u32 = 2; assert_eq!(i.pow(2), 4); let n = Meters(i); // `pow` 定义在 u32 上,直接调用会报错 assert_eq!(n.pow(2), 4); }
- 🌟🌟
newtype能在编译期保证传入的值类型正确
/* 让它工作 */ struct Years(i64); struct Days(i64); impl Years { pub fn to_days(&self) -> Days { Days(self.0 * 365) } } impl Days { pub fn to_years(&self) -> Years { Years(self.0 / 365) } } // 检查年龄(单位:年),必须接收 Years fn old_enough(age: &Years) -> bool { age.0 >= 18 } fn main() { let age = Years(5); let age_days = age.to_days(); println!("Old enough {}", old_enough(&age)); println!("Old enough {}", old_enough(&age_days)); }
- 🌟🌟
use std::ops::Add; use std::fmt::{self, format}; struct Meters(u32); impl fmt::Display for Meters { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "There are still {} meters left", self.0) } } impl Add for Meters { type Output = Self; fn add(self, other: Meters) -> Self { Self(self.0 + other.0) } } fn main() { let d = calculate_distance(Meters(10), Meters(20)); assert_eq!(format!("{}",d), "There are still 30 meters left"); } /* 实现 calculate_distance */ fn calculate_distance
类型别名 (Type alias)
类型别名可以提升代码可读性。
#![allow(unused)] fn main() { type Thunk = Box<dyn Fn() + Send + 'static>; let f: Thunk = Box::new(|| println!("hi")); fn takes_long_type(f: Thunk) { // --snip-- } fn returns_long_type() -> Thunk { // --snip-- } }
#![allow(unused)] fn main() { type Result<T> = std::result::Result<T, std::io::Error>; }
与 newtype 不同,类型别名不会生成新类型,因此下面的代码合法:
#![allow(unused)] fn main() { type Meters = u32; let x: u32 = 5; let y: Meters = 5; println!("x + y = {}", x + y); }
- 🌟
enum VeryVerboseEnumOfThingsToDoWithNumbers { Add, Subtract, } /* 填空 */ __ fn main() { // 可以用别名访问枚举值,避免冗长名字 let x = Operations::Add; }
- 🌟🌟 Rust 有一些保留的别名,其中一个可在
impl中使用。
enum VeryVerboseEnumOfThingsToDoWithNumbers { Add, Subtract, } impl VeryVerboseEnumOfThingsToDoWithNumbers { fn run(&self, x: i32, y: i32) -> i32 { match self { __::Add => x + y, __::Subtract => x - y, } } }
DST 与不定长类型
概念较复杂,这里不展开,可参考 The Book。
- 🌟🌟🌟 动态长度数组属于 DST,无法直接使用
/* Make it work with const generics */ fn my_function(n: usize) -> [u32; usize] { [123; n] } fn main() { let arr = my_function(); println!("{:?}",arr); }
- 🌟🌟 Slice 本身是 unsized,但它的引用是定长的。
/* Make it work with slice references */ fn main() { let s: str = "Hello there!"; let arr: [u8] = [1, 2, 3]; }
- 🌟🌟 Trait 也是 unsized 类型
/* 用两种方式让它工作 */ use std::fmt::Display; fn foobar(thing: Display) {} fn main() { }