【问题标题】:Rust Lifetimes in Higher Order Functions高阶函数中的 Rust 生命周期
【发布时间】:2020-12-30 17:05:46
【问题描述】:

我有一个映射:HashMap<&str, Vec<&str>>,我正在尝试创建一个反向查找映射 HashMap<&str, &str>,它从原始向量中的每个元素指向原始键。

例如如下图

{
  "k": vec!["a", "b"]
} 

会变成

{
  "a": "k",
  "b": "k",
} 

当我这样做时效果很好:

    let substitutes: HashMap<&str, Vec<&str>> = vec![("a", vec!["b", "c"])].into_iter().collect();
    
    // Below works fine
    let mut reversed: HashMap<&str, &str> = HashMap::new();
    for (&k, v) in substitutes.iter() {
        for vv in v.iter() {
            reversed.insert(vv, k);
        }
    }

但是,如果我尝试使用高阶函数来做到这一点,它就不起作用:

    // Below doesn't work
    let reversed_2: HashMap<&str, &str> = substitutes
        .iter()
        .flat_map(|(&k, v)| v.iter().map(|&vv| (vv, k)))
        .collect();

并给出以下错误:

error[E0373]: closure may outlive the current function, but it borrows `k`, which is owned by the current function
  --> src/main.rs:18:42
   |
18 |         .flat_map(|(&k, v)| v.iter().map(|&vv| (vv, k)))
   |                                          ^^^^^      - `k` is borrowed here
   |                                          |
   |                                          may outlive borrowed value `k`
   |
note: closure is returned here
  --> src/main.rs:18:29
   |
18 |         .flat_map(|(&k, v)| v.iter().map(|&vv| (vv, k)))
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `k` (and any other referenced variables), use the `move` keyword
   |
18 |         .flat_map(|(&k, v)| v.iter().map(move |&vv| (vv, k)))
   |                                          ^^^^^^^^^^

error: aborting due to previous error; 1 warning emitted

鉴于kflat_map 处于同一范围内,我正试图弄清楚vv 可能比k 寿命更长。

对于获取我的 HOF 方法失败的原因的更多信息将非常有帮助。

Playground

【问题讨论】:

    标签: rust lifetime higher-order-functions


    【解决方案1】:

    在这种情况下,编译器的帮助是完全正确的:您需要在内部闭包上添加move

    let reversed_2: HashMap<&str, &str> = substitutes
        .iter()
        .flat_map(|(&k, v)| v.iter().map(move |&vv| (vv, k)))
        .collect();
    

    Rust Playgroud

    当您创建一个使用周围范围内的变量的匿名函数时,内联函数(称为closure)需要访问这些变量。默认情况下,它引用它们,使它们在封闭范围内可用。但是,在这种情况下,闭包会引用 k 并返回它,这意味着它可以逃脱封闭范围。

    .flat_map(|(&k, v)| {
        // k is only valid in this anonymous function
    
        // a reference to k is returned out of map and also out of flat_map
        // (Note: no move)
        v.iter().map(|&vv| (vv, k))
    
        // the anonymous function ends, all references to k need to have ended
    })
    
    // A reference to k has been returned, even though k's lifetime has ended
    // Compiler error!
    

    当您切换到 move 闭包时,您不会引用封闭环境中的事物,而是获得事物本身的所有权。这使您可以返回 k 而不是对 k 的引用,从而回避问题。

    .flat_map(|(&k, v)| {
        // k is only valid in this anonymous function
    
        // k is moved out of the enclosing function and into the inner function
        // Because it owns k, it can return it
        // (Note: uses move)
        v.iter().map(move |&vv| (vv, k))
    
        // k has been moved out of this environment, so its lifetime does not end here
    })
    
    // k has been returned and is still alive
    

    【讨论】:

    • 这是否意味着我实际上是在从原始地图创建k 的副本?
    • 另外,非常感谢ddulaney!我之前尝试过添加move,但我仍然使用我的代码的先前版本.flat_map(|(&amp;k, &amp;v| v.iter().map(move |&amp;vv| (vv, k)))Playground,因此它导致了不同类型的错误。现在一切正常!
    • 很高兴听到它有效! move 不会复制 k,它会移动它,这通常是一个非常有效的操作(在这种情况下,我认为这是一个空操作,尽管我没有验证并且这不是保证)。
    猜你喜欢
    • 2023-04-06
    • 1970-01-01
    • 2013-07-03
    • 2014-02-09
    • 2017-05-03
    • 1970-01-01
    • 1970-01-01
    • 2015-09-24
    • 1970-01-01
    相关资源
    最近更新 更多