From / Into ------ 类型间的转换
一、这些 Trait 定义了数据的什么行为
rust
pub trait From<T> {
fn from(value: T) -> Self;
}
pub trait Into<T> {
fn into(self) -> T;
}
From 的含义:"我可以从 T 转换而来。"
Into 的含义:"我可以转换到 T 去。"
两者是彼此的镜像:
rust
// 实现 From 自动提供 Into(标准库的 blanket impl)
impl<T, U> Into<U> for T where U: From<T> {
fn into(self) -> U {
U::from(self)
}
}
所以通常只需要实现 From:
rust
impl From<i32> for MyType {
fn from(value: i32) -> Self {
MyType(value)
}
}
// 自动获得:
// let x: MyType = 42.into(); // Into<MyType> for i32
二、C 中是什么
2.1 C 的强制类型转换
c
// C 的隐式转换------编译器自动执行
int x = 42;
double y = x; // int → double(隐式)
long z = x; // int → long(隐式)
// C 的显式转换(cast)------程序员写出来
double d = 3.14;
int i = (int)d; // double → int(截断)
// C 的指针转换------完全类型不安全
struct Point { int x, y; };
struct User { char name[32]; };
struct Point p = {1, 2};
struct User* u = (struct User*)&p; // ❌ 编译通过!但完全没有意义
2.2 C 的自定义转换函数
c
// C 中"类型转换"就是命名函数------没有统一接口
struct string from_cstr(const char* s); // char* → string
struct string from_int(int n); // int → string
char* to_cstr(const struct string* s); // string → char*
// 每个库命名不同:
// mylib_from_int, theirlib_int_to_string, ...
三、C 的问题
3.1 隐式转换掩盖 bug
c
// 最经典的 C 隐式转换 bug
double result = 5 / 2; // ❌ 2.0,不是 2.5
// 5 / 2 是整数除法,结果是 2(int)
// 然后隐式转为 double 2.0
// 本意是:
double result = 5.0 / 2; // 2.5
3.2 强制转换不安全
c
// C 的 cast 可以绕过所有类型检查
int* ip = malloc(sizeof(int));
char* cp = (char*)ip; // ❌ 可以,但通常没有意义
void* vp = ip; // ✅ 隐式 void* 转换
int* ip2 = vp; // ✅ void* → int*(C 中隐式,C++ 需要 cast)
// 没有任何机制阻止你写出荒谬的转换
struct Socket {
int fd;
};
struct Database {
char* host;
int port;
};
struct Socket s = { 42 };
struct Database* db = (struct Database*)&s; // ❌ 编译通过!运行时崩溃
3.3 没有统一的转换接口
c
// C 的"转换成字符串"有无数种命名
// itoa(n, buf, 10) ------ 非标准
// sprintf(buf, "%d", n) ------ 标准但笨重
// snprintf(buf, sz, "%d", n) ------ 安全版
// asprintf(&buf, "%d", n) ------ GNU 扩展
// 没有统一的 "to_string(n)" 接口
四、Rust 为什么需要这些 Trait
4.1 类型安全的转换------不是所有转换都被允许
rust
// Rust 中不能随便转换类型
let x: i32 = 42;
// let y: f64 = x; // ❌ 编译错误!不能隐式转换
let y: f64 = x as f64; // ✅ 显式 as------你要的
let y: f64 = f64::from(x); // ✅ From------也是显式的
两个类型之间有没有 From 实现,由标准库作者或类型作者决定。 不是所有转换都合理。
4.2 常用 From 实现
rust
// 标准库中的 From 实现
let s = String::from("hello"); // &str → String
let s = String::from(&my_string); // &String → String
// 数值类型间的 From
// i32 → i64 是安全的(不会丢失精度)
let x: i64 = i64::from(42i32); // ✅
// i64 → i32 不是安全的(可能溢出)
// let x: i32 = i32::from(42i64); // ❌ 没有这个 From!
// 需要用 TryFrom(下一章)
// 错误类型的转换
let err = std::io::Error::from(kind); // ErrorKind → Error
let err = String::from("error"); // &str → String
4.3 into() 的便利性------类型推导
rust
// into() 利用类型推导------上下文决定目标类型
fn takes_string(s: String) {}
// 几种写法:
takes_string("hello".to_string());
takes_string(String::from("hello"));
takes_string("hello".into()); // ✅ 编译器推导出目标类型是 String
4.4 From 作为泛型约束
rust
// 泛型函数:接受"任何可以从 &str 转换的类型"
fn parse<T: From<&str>>(s: &str) -> T {
T::from(s)
}
let x: String = parse("hello"); // ✅
// let y: i32 = parse("42"); // ❌ i32 没有 From<&str>
// 另一个例子:接受"任何可以转换为 String 的类型"
fn concat<T: Into<String>>(items: &[T]) -> String {
items.iter().map(|x| x.into()).collect::<Vec<_>>().join(", ")
}
4.5 自定义 From------实现类型安全转换
rust
struct Point {
x: f64,
y: f64,
}
// 从整数元组转换
impl From<(i32, i32)> for Point {
fn from((x, y): (i32, i32)) -> Self {
Point {
x: x as f64,
y: y as f64,
}
}
}
// 从浮点元组转换
impl From<(f64, f64)> for Point {
fn from((x, y): (f64, f64)) -> Self {
Point { x, y }
}
}
let p1 = Point::from((1, 2)); // 使用 From
let p2: Point = (3.0, 4.0).into(); // 使用 Into
五、对比
| 维度 | C 强制转换 | Rust From / Into |
|---|---|---|
| 安全性 | 无------任何类型都能 cast 到任何类型 | 有------只允许显式实现的转换 |
| 发现方式 | 靠文档和惯例 | 编译器知道------有 From 才能转换 |
| 错误处理 | 不适用(截断、丢失精度) | 无(From 不失败) |
| 泛型支持 | 无 | T: From<U> 约束 |
| 隐式/显式 | C 中隐式 + 显式 (cast) | 总是显式(from() / into()) |
| 一致性 | 各库命名不同 | 统一的 From / Into |
一句话总结 :C 的强制转换
(int)x可以让任何类型变成任何其他类型------编译器不阻止,后果由程序员承担。Rust 的From/Into建立了类型间转换的白名单 ------只有实现者明确允许的转换才能发生,且调用方式统一(Type::from(x)/x.into())。