【问题标题】:Rust function signatures and outlivingRust 函数签名和寿命
【发布时间】:2020-04-18 11:51:06
【问题描述】:

为了学习 Rust,我正在编写一个解析器组合库。现在我遇到了一个跨越复杂切片类型和所有权的问题。我有一些构建块函数希望能够单独使用:

pub fn achar(character: char) -> impl Fn(&str) -> Option<char> {
    move |input| match input.chars().next() {
        Some(c) if c == character => Some(c),
        _ => None,
    }
}

pub fn alternatives<'a, T>(
    alts: &'a [impl Fn(&'a str) -> Option<T>],
) -> impl Fn(&'a str) -> Option<T> {
    move |input| {
        for alt in alts.iter() {
            let tried = alt(input);
            if tried.is_none() {
                continue;
            }
            return tried;
        }

        None
    }
}

pub fn one_of<'a>(allowed: &'a str) -> impl Fn(&'a str) -> Option<char> {
    let v = allowed.chars().map(achar).collect::<Vec<_>>();
    alternatives(&v)
}

然后使用将如下所示:

fn main() {
    println!("{:?}", achar('f')("foo"));
    println!("{:?}", alternatives(vec![achar('f'), achar('b')])("foo"));
    println!("{:?}", one_of("foo")("foo"));
}

就像我收到以下错误:

   = note:   expected type `for<'r> fn(&'r str) -> std::option::Option<char> {any_char}`
           found reference `&impl for<'r> std::ops::Fn<(&'r str,)>`

我会说quote 实现了Fn(&amp;str),不是吗?我怎样才能让它工作?


一旦解决了下一个问题:

33 |替代品(&v) | ^^^^^^^^^^^^^^--^ | | | | | `v` 在这里借用 |返回引用当前函数拥有的数据的值

当然,内联并不能解决问题。我不知道如何攻击它。

Playground is available here.

【问题讨论】:

  • 您帖子中的代码不会导致与 Playground 中的代码相同的错误。
  • 谢谢trentcl。在写这篇文章时,我仍在努力让它发挥作用。我一定是混淆了版本。

标签: types rust closures lifetime ownership


【解决方案1】:

这里发生了很多事情:

pub fn alternatives<'a, T>(
    alts: &'a [impl Fn(&'a str) -> Option<T>],
) -> impl Fn(&'a str) -> Option<T> {
    move |input| {
        ...
    }
}

上面告诉alternatives 要求切片alts 中的所有元素都属于同一类型,并且该类型实现了Fn(&amp;'a str) -&gt; Option&lt;T&gt;。然而,在调用let escaping = alternatives(&amp;vec![any_char, &amp;quote, &amp;backslash]);(或者甚至只是vec![any_char, &amp;quote, &amp;backslash];)中,元素具有不同的类型(一个是any_char-type,下一个是quote-type,等等)。另一方面,vec![achar('f'), achar('b')] 有效,因为所有元素都有achar-type。

也就是说,即使所有元素都实现了Fn(&amp;str)-&gt;Option,它们也不完全属于同一类型。

关于下一个错误:

pub fn one_of<'a>(allowed: &'a str) -> impl Fn(&'a str) -> Option<char> {
    let v = allowed.chars().map(achar).collect::<Vec<_>>();
    alternatives(&v)
}

在这里,您返回alternatives(&amp;v) 借用 v。但是v 在one_of 的末尾超出了范围,这意味着alternatives 的结果将引用已被破坏的东西,因此rust 不允许这样做。

现在,对于alternatives接受的类型,我们能做些什么呢?

  • alternatives 可以接受盒装函数。然而,这会产生一些运行时开销,但会允许备选方案的数量在运行时是动态的。
  • alternatives 可以接受函数指针。但是,这将禁止(环境捕获)闭包。
  • alternatives 可以接受 2 个函数,并且可以通过宏提供两个以上的函数。这基本上会在编译时修复备选方案的数量,但没有运行时开销。

对于one_of中的生命周期问题我们能做些什么?

  • 这实际上也可以被认为是alternatives 中的一个问题,因为alternatives 可能应该接受一些函数作为参数并保留它们,以便调用者不必担心生命周期问题。

【讨论】:

  • 我可以忍受alternatives 获得所有权并改变了这一点。但令我惊讶的是,您不能传递所有行为相同的函数数组(即相同的签名和所有)。切片会比Vec 更好吗?
  • 只有相同签名的函数的问题是它们可能是闭包,即有些可能“捕获”变量(例如你achar捕获一个字符)。
  • 如果您想获得所有权,Vec 可能是更好的选择(因为 slice 不使用其数据,而 Vec 可以)。
猜你喜欢
  • 1970-01-01
  • 2021-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-02
  • 2015-11-18
  • 1970-01-01
相关资源
最近更新 更多