将特征用作类型时出错,但在用作where子句中的边界时则出错

Nei*_*hen 1 rust

似乎Rust编译器对where子句有不同的行为.

mod sub {
    use std::mem;

    static mut FF : *const Foo = &NopFoo;

    pub trait Foo: Send + Sync {
        fn foo(&self);
    }

    pub struct NopFoo;

    impl Foo for NopFoo {
        fn foo(&self) { println!("Nop"); }
    }

    pub struct HelloFoo {
        pub num: i64,
    }

    impl Foo for HelloFoo {
        fn foo(&self) { println!("Hello, {}", self.num ); }
    }

    pub fn set_ff<M>(make_foo: M) -> bool
        where M: FnOnce() -> Box<Foo> // <== Here
    {
        unsafe {
            FF = mem::transmute(make_foo());
        }
        false
    }

    pub fn get_ff() -> Option<&'static Foo> {
        Some(unsafe { &*FF })
    }
}

fn main() {
    sub::get_ff().unwrap().foo();

    let f = sub::HelloFoo{num: 42};
    sub::set_ff(|| Box::new(f));

    sub::get_ff().unwrap().foo();
}
Run Code Online (Sandbox Code Playgroud)

(游乐场)

有一个where条款,它工作正常,打印:

Nop
Hello, 42
Run Code Online (Sandbox Code Playgroud)

如果我wheresub::set_ff()Rust编译器报告错误中删除该子句:[E0277]和[E0308]

mod sub {
    use std::mem;

    static mut FF : *const Foo = &NopFoo;

    pub trait Foo: Send + Sync {
        fn foo(&self);
    }

    pub struct NopFoo;

    impl Foo for NopFoo {
        fn foo(&self) { println!("Nop"); }
    }

    pub struct HelloFoo {
        pub num: i64,
    }

    impl Foo for HelloFoo {
        fn foo(&self) { println!("Hello, {}", self.num ); }
    }

    pub fn set_ff(make_foo: Box<Foo>) -> bool  // <== Here
    {
        unsafe {
            FF = mem::transmute(make_foo());
        }
        false
    }

    pub fn get_ff() -> Option<&'static Foo> {
        Some(unsafe { &*FF })
    }
}

fn main() {
    sub::get_ff().unwrap().foo();

    let f = sub::HelloFoo{num: 42};
    sub::set_ff(|| Box::new(f));

    sub::get_ff().unwrap().foo();
}
Run Code Online (Sandbox Code Playgroud)

(游乐场)

我认为它应该工作正常,但编译器报告错误:

error: the trait bound `std::ops::FnOnce() -> Box<sub::Foo + 'static> + 'static: std::marker::Sized` is not satisfied [--explain E0277]
  --> <anon>:24:19
24 |>     pub fn set_ff(make_foo: FnOnce() -> Box<Foo>) -> bool
   |>                   ^^^^^^^^
note: `std::ops::FnOnce() -> Box<sub::Foo + 'static> + 'static` does not have a constant size known at compile-time
note: all local variables must have a statically known size

error: mismatched types [--explain E0308]
  --> <anon>:41:17
41 |>     sub::set_ff(|| Box::new(f));
   |>                 ^^^^^^^^^^^^^^ expected trait std::ops::FnOnce, found closure
note: expected type `std::ops::FnOnce() -> Box<sub::Foo + 'static> + 'static`
note:    found type `[closure@<anon>:41:17: 41:31 f:_]`
Run Code Online (Sandbox Code Playgroud)

为什么编译需要除锈'static,并Sized在第二个,为什么做的第一个工作?

我的OS和Rust版本:

?  ~ uname -a
Linux laptop 4.2.0-35-generic #40~14.04.1-Ubuntu SMP Fri Mar 18 16:37:35 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
?  ~ rustc --version
rustc 1.10.0-nightly (9c6904ca1 2016-05-18)
Run Code Online (Sandbox Code Playgroud)

She*_*ter 6

简短的回答:这两段代码不相同,第二段甚至没有意义.你可能想要第一个.

让我们看一个更简单的例子:

trait Foo {
    fn foo(&self) {}
}

fn in_where<T>(x: T)
    where T: Foo
{
    x.foo()
}

fn in_declaration<T: Foo>(x: T) {
    x.foo()
}

fn in_type(x: Foo) {
    x.foo()
}
Run Code Online (Sandbox Code Playgroud)

这捕获了使用的原始案例where,添加了将特征绑定在泛型声明中的相同案例,并包括将特征直接用作参数类型的失败案例.

这里的关键点是,前两个版本一样的三分之一.工作版本声明任何类型都可以通过值传递给函数,只要它实现了Foo特征即可.非工作版本声明它只接受一种类型,即特征的类型本身.

正如编译器所述:

core::marker::Sized该类型没有实现特征Foo + 'static

Foo + 'static在编译时没有已知的常量; 所有局部变量必须具有静态已知大小.

当使用其中一个工作版本时,编译器会为所使用的每种具体类型生成一个代码版本(称为单态化的过程).它知道该类型需要多少空间,并且可以在堆栈上适当地分配空间以容纳它.

但是,特征会创建与特征同名的unsized类型.编译器知道要分配多少空间,因此实际生成该函数的机器代码是不可能的.

可以使用特征类型,但只能通过间接级别(特征对象).两个常见的例子是&FooBox<Foo>.这两个都通过指针间接访问底层特征.由于指针具有已知大小,因此可以生成代码.

fn in_type_ref(x: &Foo) {
    x.foo()
}

fn in_type_box(x: Box<Foo>) {
    x.foo()
}
Run Code Online (Sandbox Code Playgroud)

进一步阅读:


为什么Rust编译器需要 'static

它没有.'static由于您尚未指定生命周期,因此在特征类型中添加了隐式绑定.参数的完整类型是Foo + 'static.


归档时间:

查看次数:

1279 次

最近记录:

6 年,5 月 前