如何为Option <closure>?</closure>指定生命周期

时间:2014-05-25 20:53:47

标签: syntax rust lifetime

我正在尝试将字段放在应该包含Option<closure>的结构上。

然而,Rust正在向我大吼大叫,我必须指明它的生命周期(并不是说我真的会这么说)。我正尽力做到这一点,但Rust对我提出的建议并不满意。看看我对我得到的编译错误的内联注释。

struct Floor{
    handler: Option<|| ->&str> //this gives: missing lifetime specifier 
    //handler: Option<||: 'a> // this gives: use of undeclared lifetime name `'a`
}

impl Floor {
    // I guess I need to specify life time here as well 
    // but I can't figure out for the life of me what's the correct syntax
    fn get(&mut self, handler: || -> &str){
        self.handler = Some(handler);
    }
}

2 个答案:

答案 0 :(得分:15)

这有点棘手。

作为一般经验法则,只要您在数据结构中存储借用的引用(即&类型),就需要命名其生命周期。在这种情况下,您使用'a走在正确的轨道上,但必须在当前范围中引入'a。它与引入类型变量的方式相同。所以要定义你的Floor结构:

struct Floor<'a> {
    handler: Option<|| -> &'a str>
}

但这里还有另一个问题。闭包本身也是一个具有生命周期的引用,也必须命名。所以这里有两种不同的生命周期!试试这个:

struct Floor<'cl, 'a> {
    handler: Option<||:'cl -> &'a str>
}

对于您的impl Floor,您需要将这些生命周期介绍到范围内:

impl<'cl, 'a> Floor<'cl, 'a> {
    fn get(&mut self, handler: ||:'cl -> &'a str){
        self.handler = Some(handler);
    }
}

你可以在技术上将其减少到一个生命周期并使用||:'a -> &'a str,但这意味着返回的&str始终与闭包本身具有相同的生命周期,我认为这是一个不好的假设

答案 1 :(得分:3)

回答当前Rust版本1.x

有两种可能性来获得你想要的东西:无盒装封口或盒装封口。未装箱的闭包非常快(大多数情况下,它们都是内联的),但它们为结构添加了一个类型参数。盒装封闭在这里增加了一点自由:它们的类型被一个间接级别擦除,遗憾的是它有点慢。

我的代码有一些示例函数,因此它有点长,请原谅;)

未装箱的关闭

完整代码:

struct Floor<F>
    where F: for<'a> FnMut() -> &'a str 
{
    handler: Option<F>,
}


impl<F> Floor<F> 
    where F: for<'a> FnMut() -> &'a str
{
    pub fn with_handler(handler: F) -> Self {
        Floor {
            handler: Some(handler),
        }
    }

    pub fn empty() -> Self {
        Floor {
            handler: None,
        }
    }

    pub fn set_handler(&mut self, handler: F) {
        self.handler = Some(handler);
    }

    pub fn do_it(&mut self) {
        if let Some(ref mut h) = self.handler {
            println!("Output: {}", h());
        }
    }
}

fn main() {
    let mut a = Floor::with_handler(|| "hi");
    a.do_it();

    let mut b = Floor::empty();
    b.set_handler(|| "cheesecake");
    b.do_it();
}

现在这有一些典型的问题:你不能简单地拥有Vec多个Floor,并且使用Floor对象的每个函数都需要有类型参数& #39;自己的。另外:如果删除行b.set_handler(|| "cheesecake");,代码将无法编译,因为编译器缺少b的类型信息。

在某些情况下,您不会遇到这些问题 - 在其他情况下,您需要另外解决方案。

盒装封闭

完整代码:

type HandlerFun = Box<for<'a> FnMut() -> &'a str>;

struct Floor {
    handler: Option<HandlerFun>,
}

impl Floor {
    pub fn with_handler(handler: HandlerFun) -> Self {
        Floor {
            handler: Some(handler),
        }
    }

    pub fn empty() -> Self {
        Floor {
            handler: None,
        }
    }

    pub fn set_handler(&mut self, handler: HandlerFun) {
        self.handler = Some(handler);
    }

    pub fn do_it(&mut self) {
        if let Some(ref mut h) = self.handler {
            println!("Output: {}", h());
        }
    }
}

fn main() {
    let mut a = Floor::with_handler(Box::new(|| "hi"));
    a.do_it();

    let mut b = Floor::empty();
    b.set_handler(Box::new(|| "cheesecake"));
    b.do_it();
}

它有点慢,因为我们为每个闭包都有一个堆分配,当调用盒装闭包时,它大部分时间是间接调用(CPU不喜欢间接调用.. )。

Floor结构没有类型参数,因此您可以拥有Vec个结构。您也可以删除b.set_handler(Box::new(|| "cheesecake"));,它仍然可以使用。