为什么Rust中Trait默认不具有Sized约束,而函数却默认具有?
我定义了一个名为ReadTag的Trait,其中包含一个返回Self类型的函数,但编写后出现了编译错误:
trait ReadTag { fn read_out(buf: &mut &[u8]) -> Option<Self>; }
编译错误信息:
error[E0277]: the size for values of type `Self` cannot be known at compilation time --> src/lib.rs:2:37 | 2 | fn read_out(buf: &mut &[u8]) -> Option<Self>; | ^^^^^^^^^^^^ doesn't have a size known at compile-time
添加Sized作为超Trait可以解决该错误,这符合逻辑,但我疑惑为何Trait默认不像函数那样带有Sized约束?比如下面的函数示例:
fn my_sized<T>(t: T) { } // 正常工作,Sized是默认启用,需手动选择关闭 :) fn my_unsized<T: ?Sized>(t: T) { } // 不允许这样写
问题解答
核心原因:Trait的设计目标是支持动态分发(DST)
Rust中Trait的默认设计是允许被**动态大小类型(DST)**实现,而函数的默认约束是为了保证栈上分配的安全性和性能。
函数的默认Sized约束:函数参数如果是
T类型,默认要求T: Sized,因为栈上分配必须知道类型的精确大小。如果要支持DST,必须显式添加?Sized注解,但直接传递T类型参数依然不可行——因为DST无法直接在栈上存储,通常需要通过引用(如&T)来操作。Trait的无默认Sized约束:Trait需要支持作为trait object使用(比如
dyn ReadTag),而trait object本身就是一种典型的DST。如果Trait默认带有Sized约束,那么所有trait object的使用都会被禁止,这会彻底废掉Trait的动态分发能力——这可是Trait的核心特性之一。
你的read_out函数返回Option<Self>,而Option<T>要求T必须是Sized类型(因为它需要在内存中直接存储T的实例),所以编译器会报错。添加Sized作为超约束后,相当于明确这个Trait只能被Sized类型实现,自然就满足了返回值的大小要求。
总结
函数默认Sized是为了栈上操作的安全和高效,而Trait默认不要求Sized是为了支持动态分发和DST类型的实现,二者的设计目标不同,才导致了这个约束差异。
内容的提问来源于stack exchange,提问作者kalkronline

