【问题标题】:Spurious requirement for std::marker::Sized对 std::marker::Sized 的虚假要求
【发布时间】:2015-06-05 06:50:11
【问题描述】:

更新:这个更简单的代码 (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");
                        ^~~~~

但我认为这里没有任何理由要求尺寸。 a 和b 处理相同,属于同一类型,a 没有问题。所以

  • 为什么b 有问题,以及
  • 我如何告诉它 Trait 的大小不应该是已知的。

事实上,它是一个 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");

【问题讨论】:

    标签: compiler-errors rust traits


    【解决方案1】:

    错误不是虚假的。

    特征对象的原始表示是这样的 (std::raw::TraitObject):

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

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

    但是动态大小的类型呢?以切片(&amp;[T])为例,它们是这样的形式:

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

    这个引用是两个字:一个指向切片开始的指针和切片中的元素个数。

    &amp;T 的大小因此实际上并不是恒定的。如果T 是Sized,&amp;T 和Box&lt;T&gt; 将是一个词,但如果T 不是Sized,&amp;T 和Box&lt;T&gt; 将是两个 词.

    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);
    

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

    因此:如果你想创建一个 trait 对象,你需要使用一个大小的类型,比如&amp;str,而不是一个动态大小的类型,比如str。这意味着像(x: &amp;&amp;str) as &amp;std::fmt::Display;:

    arg("foo", &"bar");
    

    【讨论】:

    • 谢谢。不幸的是,&amp;&amp;str 无济于事,因为实际的字符串具有必要的生命周期,但对该引用的引用却没有。所以我得把它包起来。如果我能把它包在什么东西里就好了。如果可以在没有额外分配的情况下完成,那就太好了,但我担心不会。
    • ... 再想一想,内部引用是一个临时的并且一直保留到语句结束,所以在生成的&amp;'a&amp;str 中,'a 应该足够长以便返回。现在我只需要确定是否需要在 impl Trait for &amp;str 中修改生命周期来完成这项工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-29
    • 2016-09-12
    • 2013-02-10
    • 2018-06-26
    • 2016-02-27
    相关资源
    最近更新 更多