【问题标题】:"temporary value dropped while borrowed" with HashMap<&str, &dyn Fn(&str) -> bool>HashMap<vs str, dyn Fn(&str) -> bool> 的“借用时临时值下降”
【发布时间】:2021-03-18 02:22:21
【问题描述】:

我有一个HashMap 的函数字符串。据我了解,&amp;dyn Fn(&amp;str) -&gt; bool 是必要的,因为我想同时使用函数和闭包,但是我得到了这个编译错误:

error[E0716]: temporary value dropped while borrowed
  --> src/test.rs:22:26
   |
22 |     checks.insert("k3", &|i| cached_regex.is_match(i));
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
   |                          |
   |                          creates a temporary which is freed while still in use
...
27 |         match checks.get(kvp[0]) {
   |               ------ borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value

示例代码:

use regex::Regex;
use std::collections::HashMap;

fn main() {
    assert_eq!(run_checks("k1:test, k3:1234"), true);
    assert_eq!(run_checks("k1:test, k3:12345"), false);
    assert_eq!(run_checks("k1:test, k2:test2"), true);
}

fn specific_check(i: &str) -> bool { i == "test" }

fn run_checks(input: &str) -> bool {
    let cached_regex = Regex::new(r"^\d{4}$").unwrap();

    let mut checks: HashMap<&str, &dyn Fn(&str) -> bool> = HashMap::new();
    checks.insert("k1", &specific_check);
    checks.insert("k2", &|i| i == "test2");

    // Not working
    checks.insert("k3", &|i| cached_regex.is_match(i));

    for kvp_pair in input.split(",") {
        let kvp: Vec<&str> = kvp_pair.trim().split(":").collect();

        match checks.get(kvp[0]) {
            Some(check) => {
                if !check(kvp[1]) {
                    return false;
                }
            }
            None => return false,
        }
    }

    true
}

【问题讨论】:

    标签: rust closures lifetime borrow-checker trait-objects


    【解决方案1】:

    解决这个问题最直接的方法是Boxdyn Fn(&amp;str) -&gt; bool trait 对象。固定工作示例:

    use regex::Regex;
    use std::collections::HashMap;
    
    fn main() {
        assert_eq!(run_checks("k1:test, k3:1234"), true);
        assert_eq!(run_checks("k1:test, k3:12345"), false);
        assert_eq!(run_checks("k1:test, k2:test2"), true);
    }
    
    fn specific_check(i: &str) -> bool { i == "test" }
    
    fn run_checks(input: &str) -> bool {
        let cached_regex = Regex::new(r"^\d{4}$").unwrap();
    
        let mut checks: HashMap<&str, Box<dyn Fn(&str) -> bool>> = HashMap::new();
        checks.insert("k1", Box::new(specific_check));
        checks.insert("k2", Box::new(|i| i == "test2"));
        
        // now works
        checks.insert("k3", Box::new(|i| cached_regex.is_match(i)));
    
        for kvp_pair in input.split(",") {
            let kvp: Vec<&str> = kvp_pair.trim().split(":").collect();
    
            match checks.get(kvp[0]) {
                Some(check) => {
                    if !check(kvp[1]) {
                        return false;
                    }
                }
                None => return false,
            }
        }
    
        true
    }
    

    playground


    解决方案背后的详细解释

    此行中的类型注释不完整,因为省略了引用和特征对象本身的生命周期:

    let mut checks: HashMap<&str, &dyn Fn(&str) -> bool> = HashMap::new();
    

    鉴于您插入checks 的键和值,Rust 推断checks 的完整类型为HashMap&lt;&amp;'static str, &amp;'static (dyn for&lt;'a&gt; Fn(&amp;'a str) -&gt; bool + 'static)&gt;。前两个插入匹配此类型签名,但最后第三个不匹配。第三次插入中使用的值的类型为&amp;'static (dyn for&lt;'a&gt; Fn(&amp;'a str) -&gt; bool + 'b),其中'b 表示捕获的cached_regex 变量的生命周期。 Rust 抛出编译器错误的原因是因为创建对具有非'static 生命周期的某些类型的'static 引用是无效的,因为引用可能会失效。将 trait 对象装箱避免了这个问题,因为我们不再需要创建一个 'static 对其的引用,因此不再需要 trait 对象本身需要为 'static 生存,并且可以安全地进行最后的第三次插入。

    【讨论】:

    • 你能解释一下为什么在最后一种情况下必须用“k3”而不是“k2”来装箱吗?
    猜你喜欢
    • 1970-01-01
    • 2019-06-26
    • 2021-03-05
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 2019-11-29
    • 1970-01-01
    • 2021-05-05
    相关资源
    最近更新 更多