从C到Rust:Trait From Into 类型转换

28 阅读4分钟

From / Into —— 类型间的转换

一、这些 Trait 定义了数据的什么行为


pub trait From<T> {

fn from(value: T) -> Self;

}

  


pub trait Into<T> {

fn into(self) -> T;

}

From 的含义:"我可以从 T 转换而来。"

Into 的含义:"我可以转换到 T 去。"

两者是彼此的镜像:


// 实现 From 自动提供 Into(标准库的 blanket impl)

impl<T, U> Into<U> for T where U: From<T> {

fn into(self) -> U {

U::from(self)

}

}

所以通常只需要实现 From


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 的隐式转换——编译器自动执行

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 中"类型转换"就是命名函数——没有统一接口

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 隐式转换 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 的 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 的"转换成字符串"有无数种命名

// 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 中不能随便转换类型

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 实现


// 标准库中的 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() 的便利性——类型推导


// into() 利用类型推导——上下文决定目标类型

fn takes_string(s: String) {}

  


// 几种写法:

takes_string("hello".to_string());

takes_string(String::from("hello"));

takes_string("hello".into()); // ✅ 编译器推导出目标类型是 String

4.4 From 作为泛型约束


// 泛型函数:接受"任何可以从 &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——实现类型安全转换


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())。