Newtype 是 Rust 里最轻的抽象:一个只有一个字段的元组结构体。它能让「米」和「秒」不再互相混淆,能让你为别人的类型实现别人的 trait。这篇用真实的编译结果逐条验证它的好处、零成本,以及它带来的小麻烦(失去内部类型的方法)和解法(Deref)。
源码:
1 | type Meters = f64;
2 | type Seconds = f64;
3 | fn speed(d: Meters, t: Seconds) -> f64 { d / t }
4 | fn main() {
5 | let m: Meters = 100.0; let s: Seconds = 9.58;
6 | println!("正确的顺序 speed(m, s) = {:.2}", speed(m, s));
7 | println!("参数写反了 speed(s, m) = {:.4}(编译器没有报任何错误)", speed(s, m));
8 | }
编译:成功
运行(退出码 0):
正确的顺序 speed(m, s) = 10.44
参数写反了 speed(s, m) = 0.0958(编译器没有报任何错误)
源码:
1 | struct Meters(f64);
2 | struct Seconds(f64);
3 | fn speed(d: Meters, t: Seconds) -> f64 { d.0 / t.0 }
4 | fn main() {
5 | let m = Meters(100.0); let s = Seconds(9.58);
6 | println!("{:.2}", speed(s, m));
7 | }
编译:失败
error[E0308]: arguments to this function are incorrect
--> <实验目录>/src/u12b.rs:6:23
|
6 | println!("{:.2}", speed(s, m));
| ^^^^^ - - expected `Seconds`, found `Meters`
| |
| expected `Meters`, found `Seconds`
|
note: function defined here
--> <实验目录>/src/u12b.rs:3:4
|
3 | fn speed(d: Meters, t: Seconds) -> f64 { d.0 / t.0 }
| ^^^^^
help: swap these arguments
|
6 - println!("{:.2}", speed(s, m));
6 + println!("{:.2}", speed(m, s));
|
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`. 编译:成功
运行(退出码 0):
size_of::<f64>() = 8,size_of::<Meters>() = 8
两个函数共同的机器码: fadd d0, d0, d1 ; ret
汇编里的别名行:`_add_raw = _add_new`(LLVM 发现两个函数完全相同,把其中一个合并成另一个的别名)「零成本抽象」的更多例子见Zero-Cost Abstraction。
源码:
1 | use std::fmt;
2 | impl fmt::Display for Vec<String> {
3 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[{}]", self.join(", ")) }
4 | }
5 | fn main() {}
编译:失败
error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> <实验目录>/src/u12e.rs:2:1
|
2 | impl fmt::Display for Vec<String> {
| ^^^^^^^^^^^^^^^^^^^^^^-----------
| |
| `Vec` is not defined in the current crate
|
= note: impl doesn't have any local type before any uncovered type parameters
= note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules
= note: define and implement a trait or new type instead
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0117`.
源码:
1 | use std::fmt;
2 | struct Wrapper(Vec<String>);
3 | impl fmt::Display for Wrapper {
4 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[{}]", self.0.join(", ")) }
5 | }
6 | fn main() {
7 | let w = Wrapper(vec!["hello".to_string(), "world".to_string()]);
8 | println!("w = {w}");
9 | }
编译:成功
运行(退出码 0):
w = [hello, world]孤儿规则本身见Trait 实现冲突与孤儿规则。
源码:
1 | struct Names(Vec<String>);
2 | fn main() {
3 | let n = Names(vec!["a".into()]);
4 | println!("{}", n.len());
5 | }
编译:失败
error[E0599]: no method named `len` found for struct `Names` in the current scope
--> <实验目录>/src/u12g.rs:4:22
|
1 | struct Names(Vec<String>);
| ------------ method `len` not found for this struct
...
4 | println!("{}", n.len());
| ^^^ method not found in `Names`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `len`, perhaps you need to implement it:
candidate #1: `ExactSizeIterator`
help: one of the expressions' fields has a method of the same name
|
4 | println!("{}", n.0.len());
| ++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0599`.
源码:
1 | use std::ops::Deref;
2 | struct Names(Vec<String>);
3 | impl Deref for Names { type Target = Vec<String>; fn deref(&self) -> &Vec<String> { &self.0 } }
4 | fn main() {
5 | let n = Names(vec!["a".into(), "b".into()]);
6 | println!("len = {},first = {:?},iter 拼接 = {}", n.len(), n.first(), n.iter().cloned().collect::<Vec<_>>().join("+"));
7 | }
编译:成功
运行(退出码 0):
len = 2,first = Some("a"),iter 拼接 = a+b为所有 newtype 都实现 Deref 并不总是好主意(它让内部类型的方法全部暴露出来,削弱了封装);这里只验证了它能转发,没有讨论取舍。