Rust 为什么在这个特征中“大小”界限是必要的?

Rust 为什么在这个特征中“大小”界限是必要的?,rust,traits,Rust,Traits,我有两个相关功能的特点: trait WithConstructor: Sized { fn new_with_param(param: usize) -> Self; fn new() -> Self { Self::new_with_param(0) } } 为什么第二个方法(new())的默认实现会强制我将size绑定到类型上?我想这是因为堆栈指针操作,但我不确定 如果编译器需要知道堆栈上分配内存的大小, 为什么以下示例不要求T使用s

我有两个相关功能的特点:

trait WithConstructor: Sized {
    fn new_with_param(param: usize) -> Self;

    fn new() -> Self {
        Self::new_with_param(0)
    }
}
为什么第二个方法(
new()
)的默认实现会强制我将
size
绑定到类型上?我想这是因为堆栈指针操作,但我不确定

如果编译器需要知道堆栈上分配内存的大小, 为什么以下示例不要求
T
使用
size

struct SimpleStruct<T> {
    field: T,
}

fn main() {
    let s = SimpleStruct { field: 0u32 };
}
struct SimpleStruct{
字段:T,
}
fn main(){
设s=SimpleStruct{field:0u32};
}

让我们看看如果您使用非大小的类型执行此操作会发生什么

new()。但是,除非类型已调整大小,否则应该移动多少字节?我们根本不知道。这就是为什么移动语义需要
size
类型


注意:各种
都是专门为这个问题提供运行时服务的。

正如您可能已经知道的,Rust中的类型可以调整大小,也可以不调整大小。顾名思义,未大小化的类型没有存储编译器已知的这种类型的值所需的大小。例如,
[u32]
u32
s的非大小数组;因为元素的数量没有在任何地方指定,所以编译器不知道它的大小。另一个示例是裸特征对象类型,例如,
Display
,当它直接用作类型时:

let x: Display = ...;
在这种情况下,编译器不知道这里实际使用的是哪种类型,它被擦除,因此它不知道这些类型的值的大小。上述行无效-在不知道其大小的情况下,无法生成局部变量(以便在堆栈上分配足够的字节),并且无法将未大小类型的值作为参数传递到函数中或从函数返回

但是,可以通过指针使用未大小的类型,该指针可以携带附加信息—片的可用数据长度(
&[u32]
)或指向虚拟表的指针(
)。因为指针总是有一个固定的已知大小,所以它们可以存储在局部变量中,并传递到函数中或从函数中返回

给定任何具体的类型,你总是可以说它是大小的还是不大小的。然而,对于泛型,出现了一个问题-是否有一些类型参数大小

fn generic_fn<T>(x: T) -> T { ... }
这是因为在绝大多数情况下,您希望调整通用参数的大小。但是,有时您可能希望选择退出Sizeness,这可以通过
?Sized
绑定完成:

fn generic_fn<T: ?Sized>(x: &T) -> u32 { ... }
然而,这只是出于方便和实用的目的。可以将特征定义为始终采用一个类型参数,而不指定实现特征的类型:

// this is not actual Rust but some Rust-like language

trait A<T> {
    fn do_something(t: &T);
}

struct X;
impl A<X> {
    fn do_something(t: &X) {}
}
这就是规模问题的所在。
Self
参数的大小是否合适

事实证明,在Rust中,
Self
的默认大小不是。每个特质都有一个隐式的
?大小
绑定在
自我
上。之所以需要这样做,其中一个原因是,有很多特性可以用于未大小的类型,并且仍然有效。例如,任何只包含只通过引用获取并返回
Self
的方法的trait都可以用于未大小的类型。你可以阅读更多关于动机的文章

当您只定义特征及其方法的签名时,大小不是问题。因为这些定义中没有实际的代码,编译器不能假设任何东西。但是,当您开始编写使用此特性的泛型代码时(其中包括默认方法,因为它们采用隐式
Self
参数),您应该考虑大小。因为默认情况下,
Self
没有大小,所以默认的trait方法不能按值返回
Self
,也不能按值将其作为参数。因此,您需要指定默认情况下必须调整
Self
的大小:

trait A: Sized { ... }
或者,您可以指定仅当
Self
的大小为:

trait WithConstructor {
    fn new_with_param(param: usize) -> Self;

    fn new() -> Self
    where
        Self: Sized,
    {
        Self::new_with_param(0)
    }
}

但是为什么它不抱怨
新的带有_param的
?它还需要在调用方的堆栈上保留适当的空间。所以我的想法是正确的,但是为什么在泛型结构中不需要
Size
??我更新了这个问题。@Matthieu M.
new\u with_param
只是一个trait方法定义,而不是一个实现。@AndreaP:a
struct
在默认情况下总是
size
。我想我明白了。显然,泛型类型
T
(不是结构)在默认情况下被视为针对结构的大小(除非您放置
?size
),而不是针对特征。谢谢你这么完整的回答。我不知道所有的“默认大小,但自我不是”部分。“这就是我感到困惑的主要原因。”不幸的是,弗拉基米尔和铁锈书的章节已经冻结。否则你一定要考虑在那里提出你的解释。
trait A {
    fn do_something(t: &Self);
}
trait A: Sized { ... }
trait WithConstructor {
    fn new_with_param(param: usize) -> Self;

    fn new() -> Self
    where
        Self: Sized,
    {
        Self::new_with_param(0)
    }
}