请求解析Rust类型检查器与Trait检查器禁用代码细节
解析Rust中两段“绕过类型/ trait检查”的代码片段
最近碰到两段被调侃为“Rust类型检查作弊码”的代码,新手完全摸不着头脑,下面详细拆解这两段代码的实现细节:
一、“禁用类型检查器”的代码
这段代码能让编译器允许任意类型的内存转换,比如把Vec<u8>直接转成String:
pub trait __ { type __<L, R>: __<__<L, ()> = R>; fn transmute<R>(self) -> R; } impl<T> __ for T { type __<L: __<__<<R::__<R, L> as __>::__<R, ()>, ()> = R>, R: __> = L; fn transmute<R: __>(self) -> <R::__<R, Self> as __>::__<R, ()> { self } } fn main() { let s: String = vec![65_u8, 66, 67].transmute(); println!("{}", s); // prints "ABC" }
核心逻辑拆解
Trait定义:
- 定义了名为
__的trait,包含一个关联类型__<L, R>,要求该关联类型必须实现__trait,且满足__<L, ()> = R(即把L和()传入关联类型后结果等于R)。 - 同时定义
transmute<R>方法,声称可以将self转换为任意R类型。
- 定义了名为
全局Impl:
- 给所有类型实现了
__trait,这是核心前提:任何类型都自动拥有该trait的方法和关联类型。 - 关联类型
__<L, R>的定义用了类型推导花招:通过嵌套的关联类型约束,让编译器在推导时被迫接受L作为最终的关联类型结果,完全忽略L和R的实际类型关系。 transmute方法的返回值类型<R::__<R, Self> as __>::__<R, ()>,结合关联类型的定义,编译器会推导这个返回值等于R——但这完全是利用类型系统的复杂度漏洞,没有做任何实际的类型安全检查。
- 给所有类型实现了
Main函数调用:
vec![65_u8,66,67].transmute()被赋值给String类型变量s,编译器通过复杂的关联类型推导,最终认为转换合法。但本质上这是不安全的内存强制转换,只是利用编译器无法拆解复杂类型约束的漏洞绕开了检查。这里能成功打印是因为Vec<u8>和String的内存布局恰好兼容(都是指针+长度+容量),换其他类型大概率会直接panic。
二、“同时禁用trait检查器”的代码
这段代码能让你在没有trait约束的泛型函数里调用需要特定trait的方法:
pub trait __ { type __<L, R>: __<__<L, ()> = R>; fn debug<R: std::fmt::Debug>(&self); } impl<T> __ for T { type __<L: __<__<<R::__<R, L> as __>::__<R, ()>, ()> = R>, R: __> = L; fn debug<R: __>(&self) where <R::__<R, Self> as __>::__<R, ()>: std::fmt::Debug, { println!("{:?}", self); } } fn main() { let v = vec![1, 2, 3, 4, 5]; generic_without_bounds(&v); } fn generic_without_bounds<T>(x: &T) { x.debug::<()>(); // who needs trait bounds, anyway? // ^^^ prints [1, 2, 3, 4, 5] }
核心逻辑拆解
Trait定义:
- 同样定义
__trait,关联类型和前一段一致;新增debug<R>方法,原签名要求R实现Debug,但方法功能是打印self。
- 同样定义
全局Impl:
- 给所有类型实现
__trait,debug方法的签名做了手脚:把原定义中R: Debug的约束,改成在where子句中要求<R::__<R, Self> as __>::__<R, ()>: Debug。 - 当调用
debug::<()>时,R是()类型,结合关联类型推导,<()::__<(), Self> as __>::__<(), ()>会被编译器推导为Self——所以where子句的约束就变成了Self: Debug,但这个约束是隐式推导出来的,而非显式写在泛型参数上。
- 给所有类型实现
泛型函数调用:
generic_without_bounds<T>没有给T加Debug约束,但调用x.debug::<()>()时,编译器会通过关联类型推导,自动检查Self(即T)是否实现了Debug——但因为这个约束藏在trait方法的where子句里,而非泛型函数的参数约束中,所以看起来像是绕过了trait检查。本质是利用类型推导的延迟检查特性,把trait约束从泛型函数转移到了方法调用的推导过程中。
内容的提问来源于stack exchange,提问作者oᴉɹǝɥɔ
相关产品推荐
相关产品推荐

