std :: marker :: Sized的虚假要求

时间:2015-03-31 19:32:34

标签: compiler-errors rust traits

更新:这个更简单的代码(play)表现出相同的症状:

use std::fmt::Display;

pub fn arg(a: &str, b: &Display) {
}

fn main() {
    arg("foo", "bar");
}

给出错误:

<anon>:7:16: 7:21 error: the trait `core::marker::Sized` is not implemented for the type `str` [E0277]
<anon>:7     arg("foo", "bar");
                        ^~~~~
<anon>:7:16: 7:21 note: `str` does not have a constant size known at compile-time
<anon>:7     arg("foo", "bar");
                        ^~~~~

但我认为没有理由要求这里的尺寸。 ab的处理方式相同,类型相同,a没有问题。所以

  • 为什么b
  • 有问题
  • 我怎么告诉它Trait不会有大小已知。

事实上,它是一种特质类型,无论基础类型是什么,它都不会知道类型。那么它为什么要大小?

哦,我不应该忘记:在实际的用例中我需要动态多态。函数(方法)将使用不同实际类型的参数和存储的引用进行链接(请参阅下面的原始图示),因此我无法将其转换为T: Trait + ?Sized


原始展览:

我有一些代码如下(see also on play.rust-lang.org):

pub trait Trait { /* some methods */ }

impl Trait for str { /* some implementation */ }

pub struct Struct<'a> {
    args: HashMap<&'a str, &'a Trait>,
}

impl<'a> Struct<'a> {
    pub fn new() -> Self {
        Struct { args: HashMap::new() }
    }

    pub fn arg(mut self, key: &'a str, value: &'a Trait) -> Struct<'a> {
        self.args.insert(key, value);
        return self;
    }

    // of course there is something to process the collected arguments too
}

fn main() {
    Struct::new().arg("foo", "bar");
}

这给了我错误:

test.rs:32:30: 32:35 error: the trait `core::marker::Sized` is not implemented for the type `str` [E0277]
test.rs:32     Struct::new().arg("foo", "bar");
                                             ^~~~~
test.rs:32:30: 32:35 note: `str` does not have a constant size known at compile-time
test.rs:32     Struct::new().arg("foo", "bar");

1 个答案:

答案 0 :(得分:4)

错误并非虚假。

特征对象的原始表示形式为(std::raw::TraitObject):

#[repr(C)]
pub struct TraitObject {
    pub data: *mut (),
    pub vtable: *mut (),
}

对象的实际数据位于单个指针后面。

但动态大小的类型怎么样?以切片(&[T])为例,它们具有以下形式:

#[repr(C)]
pub struct Slice<T> {
    pub data: *const T,
    pub len: usize,
}

此引用是两个字:指向切片开头的指针和切片中元素的数量

&T的大小因此实际上不是常数。如果TSized&TBox<T>将是一个字,但如果T不是Sized,则&TBox<T>两个字。

let word = std::mem::size_of::<usize>();

// References to sized types: one word.
assert_eq!(std::mem::size_of::<&()>(), 1 * word);
assert_eq!(std::mem::size_of::<&u8>(), 1 * word);
assert_eq!(std::mem::size_of::<&String>(), 1 * word);

// References to unsized types: two words.
assert_eq!(std::mem::size_of::<&[u8]>(), 2 * word);
assert_eq!(std::mem::size_of::<&str>(), 2 * word);
assert_eq!(std::mem::size_of::<&std::path::Path>(), 2 * word);

这有什么影响?好吧,如前所述,特征对象的定义要求数据指针只有一个字长。要存储动态大小的类型,需要两个字;我没有太多考虑是否可能来膨胀所有特征对象以使数据有两个单词(其中一个在大小对象的情况下是多余的)的实用性,所以它可能或可能不是,但该语言决定不支持动态大小类型的特征对象。

因此:如果您希望创建特征对象,则需要使用大小类型(例如&str),而不是动态大小的类型,例如str。这意味着像(x: &&str) as &std::fmt::Display;

这样的事情
arg("foo", &"bar");