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

时间:2016-05-20 13:14:56

标签: rust

似乎Rust编译器在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(); } 子句上有不同的行为。

where

Playground

使用Nop Hello, 42 子句,它工作正常,打印:

where

如果我从sub::set_ff()删除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(); } 子句,则Rust编译器报告错误:[E0277]和[E0308]

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:_]`

Playground

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

'static

为什么Rust编译器在第二个编译器中需要Sized➜ ~ 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) ,为什么第一个编译器有效?

我的操作系统和Rust版本:

$stateProvider
  .state('home', {
    url: '/',
    views: {
      '': {
        templateUrl: 'templates/app.tpl.html',
      },
      'section1': {
        controller: 'Section1Controller as vm',
        templateUrl: 'templates/section1.tpl.html'
      },
      'section2': {
        controller: 'Section2Controller as vm',
        templateUrl: 'templates/section2.tpl.html'
      },
      'section3': {
        controller: 'Section3Controller as vm',
        templateUrl: 'templates/section3.tpl.html'
      },
      'section4': {
        controller: 'Section4Controller as vm',
        templateUrl: 'templates/section4.tpl.html'
      }
    }
  })
  .state('page2', {
    url: '/page2',
    views: {
      '': {
        templateUrl: 'templates/page2.tpl.html',
      },
      'section1': {
        controller: 'Section1Controller as vm',
        templateUrl: 'templates/section1.tpl.html'
      },
      'section2': {
        controller: 'Section2Controller as vm',
        templateUrl: 'templates/section2.tpl.html'
      },
      'section3': {
        controller: 'Section3Controller as vm',
        templateUrl: 'templates/section3.tpl.html'
      }
    }
  })

1 个答案:

答案 0 :(得分: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()
}

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

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

正如编译器所述:

  

特征core::marker::Sized未针对类型Foo + 'static

实施      

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

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

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

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

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

fn in_type_box(x: Box<Foo>) {
    x.foo()
}

进一步阅读:

  

为什么Rust编译器需要'static

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