【问题标题】:"expected bound lifetime parameter, found concrete lifetime" on StreamExt .scan() methodStreamExt .scan() 方法上的“预期绑定生命周期参数,找到具体生命周期”
【发布时间】:2020-11-21 09:45:06
【问题描述】:

我正在使用 async-tungstenite 监听 websocket,并使用 async-std 的 StreamExt 对结果流进行操作。

我想使用 HashMap 从 websocket 中累积最新的 Ticker 值。稍后将查找这些 Ticker 值以用于计算。我使用 Ticker 结构的符号(字符串)值作为 HashMap 的键。我正在使用 .scan StreamExt 方法进行累积。

但是,我收到与生命周期相关的编译错误。这是一些精简的代码:

        let tickers = HashMap::new();
        let mut stream = ws.
            .scan(tickers, accumulate_tickers);
        while let msg = stream.next().await {
            println!("{:?}", msg)
        }

...以及accumulate_tickers函数:

fn accumulate_tickers(tps: &mut HashMap<String, Ticker>, bt: Ticker) -> Option<&HashMap<String, Ticker>> {
    tps.insert((*bt.symbol).to_string(), bt);
    Some(tps)
}

我收到的编译错误如下:

error[E0271]: type mismatch resolving `for<'r> <for<'s> fn(&'s mut std::collections::HashMap<std::string::String, ws_async::model::websocket::Ticker>, ws_async::model::websocket::Ticker) -> std::option::Option<&'s std::collections::HashMap<std::string::String, ws_async::model::websocket::Ticker>> {accumulate_tickers} as std::ops::FnOnce<(&'r mut std::collections::HashMap<std::string::String, ws_async::model::websocket::Ticker>, ws_async::model::websocket::Ticker)>>::Output == std::option::Option<_>`
--> examples/async_std-ws.rs:64:4
   |
64 |         .scan(tickers, accumulate_tickers);
   |          ^^^^ expected bound lifetime parameter, found concrete lifetime

我不知道有一种方法可以为扫描方法提供生命周期参数。

我想知道这个问题是否可能与我修改 HashMap 然后尝试返回它的事实有关(是移动问题吗?)。我该如何解决这个问题,或者至少缩小原因?

【问题讨论】:

    标签: rust hashmap lifetime


    【解决方案1】:

    我能够得到这个工作。通过编译器错误,我最终得到了 accumulate_tickers 函数的这个签名:

    fn accumulate_tickers<'a>(tps: &'a mut &'static HashMap<String, Ticker>, bt: Ticker) -> Option<&'static HashMap<String, Ticker>>
    

    我确实希望累加器 HashMap 具有静态生命周期,这样才有意义。 tps: &amp;'a mut &amp;'static HashMap... 确实看起来有点奇怪,但它确实有效。

    然后,这个问题是 tickers 也必须有一个静态生命周期(它是累加器的初始值。我尝试在 main 之外将其声明为静态但它不允许我将其设置为函数的结果 - HashMap::new()

    然后我转向lazy_static,它允许我创建一个静态值来实现这一点:

    lazy_static! {
        static ref tickers: HashMap<String, Ticker> = HashMap::new();
    }
    

    这给了我一个具有静态生命周期的 HashMap 累加器。然而,就像在根范围内声明的普通静态值一样,它是不可变的。为了解决这个问题,我阅读了lazy_static 团队的一些提示,然后找到了https://pastebin.com/YES8dsHH。这向我展示了如何通过将静态累加器包装在 Arc&lt;Mutex&lt;_&gt;&gt; 中来使其可变。

    lazy_static! {
        // from https://pastebin.com/YES8dsHH
        static ref tickers: Arc<Mutex<HashMap<String, Ticker>>> = {
            let mut ts = HashMap::new();
            Arc::new(Mutex::new(ts))
        };
    }
    

    这确实意味着我必须在读取或修改之前从 Mutex 中检索累加器(并将其锁定),但它再次起作用。

    把它们放在一起,精简后的代码现在看起来像这样:

    #[macro_use]
    extern crate lazy_static;
    
    lazy_static! {
        // from https://pastebin.com/YES8dsHH
        static ref tickers: Arc<Mutex<HashMap<String, Ticker>>> = {
            let mut ts = HashMap::new();
            Arc::new(Mutex::new(ts))
        };
    }
    
    // SNIP
    
        // Inside main()
        let mut ticks = ws
            .scan(&tickers, accumulate_tickers);
        while let Some(msg) = ticks.next().await {
            println!("{:?}", msg.lock().unwrap());
        }
    
    // SNIP
    
    fn accumulate_tickers<'a>(tps: &'a mut &'static tickers, bt: Ticker) -> Option<&'static tickers> {
        tps.lock().unwrap().insert((*bt.symbol).to_string(), bt);
        Some(tps)
    }
    

    我很高兴听到有关如何使这变得更简单或更优雅的建议。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-22
      • 1970-01-01
      • 2014-08-05
      相关资源
      最近更新 更多