【问题标题】:Turn Vec<u64> into Vec<(&str, u64)> for tui::BarChart data将 Vec<u64> 转换为 Vec<(&str, u64)> 以获取 tui::BarChart 数据
【发布时间】:2020-05-23 21:52:47
【问题描述】:

如何将Vec&lt;u64&gt; 变成Vec&lt;(&amp;str, u64)&gt;,例如 前者的索引嵌入到后者的str 部分?

例如,[4, 9, 3] 应该变成[("0", 4), ("1", 9), ("2", 3)]

我想这样做的原因是因为我想绘制我的条形图 vector 使用the barchart from TUI,需要这样的类型。

我已经尝试了一些显而易见的事情,例如循环和推送:

fn main() {
    let my_vec: Vec<u64> = vec![4, 9, 3];
    let mut result: Vec<(&str, u64)> = Vec::new();
    for (k, v) in my_vec.iter().enumerate() {
        result.push((&k.to_string(), *v));
    }

    assert_eq!(result, [("0", 4), ("1", 9), ("2", 3)]);
}
error[E0716]: temporary value dropped while borrowed
 --> src/main.rs:5:23
  |
5 |         result.push((&k.to_string(), *v));
  |                       ^^^^^^^^^^^^^      - temporary value is freed at the end of this statement
  |                       |
  |                       creates a temporary which is freed while still in use
...
8 |     assert_eq!(result, [("0", 4), ("1", 9), ("2", 3)]);
  |     --------------------------------------------------- borrow later used here
  |
  = note: consider using a `let` binding to create a longer lived value

或使用map:

fn main() {
    let my_vec: Vec<u64> = vec![4, 9, 3];
    let result: Vec<(&str, u64)> = my_vec
        .into_iter()
        .enumerate()
        .map(|(k, v)| (&k.to_string(), v))
        .collect();

    assert_eq!(result, [("0", 4), ("1", 9), ("2", 3)]);
}
error[E0277]: a value of type `std::vec::Vec<(&str, u64)>` cannot be built from an iterator over elements of type `(&std::string::String, u64)`
 --> src/main.rs:7:10
  |
7 |         .collect();
  |          ^^^^^^^ value of type `std::vec::Vec<(&str, u64)>` cannot be built from `std::iter::Iterator<Item=(&std::string::String, u64)>`
  |
  = help: the trait `std::iter::FromIterator<(&std::string::String, u64)>` is not implemented for `std::vec::Vec<(&str, u64)>`

但无论我做什么,我似乎都无法解决终身问题, 因为k.to_string() 的寿命不够长。

当然,如果有更好的方法来获取矢量,我愿意接受建议 以索引为标签绘制。

【问题讨论】:

    标签: rust


    【解决方案1】:

    您不能直接这样做,&amp;str 借用字符串,因此在您借用字符串时字符串必须保持活动状态,一般的答案是创建您的字符串,存储它们并借用它们,例如:

    fn main() {
        let my_vec: Vec<u64> = vec![4, 9, 3];
    
        let my_owned : Vec<(String, u64)> = my_vec
            .into_iter()
            .enumerate()
            .map(|(k, v)| (k.to_string(), v))
            .collect();
    
        let result: Vec<(&str, u64)> = my_owned
            .iter()
            .map(|(k, v)| (k.as_str(), *v))
            .collect();
    
        assert_eq!(result, [("0", 4), ("1", 9), ("2", 3)]);
    }
    

    虽然这适用于您的具体情况,但 data() 很奇怪。如果不深入挖掘,很难判断是否有问题。在您链接的示例中,str 是静态的,也许它只是(或主要)打算像示例一样使用,因此您不应该将它与动态索引一起使用。

    【讨论】:

      【解决方案2】:

      &amp;str 引用必须指向比result 寿命更长的字符串。您不能使用对临时字符串的引用,因为它们被删除得太快了。

      一种选择是将所有Strings 存储在一个比result 寿命更长的集合中。预先计算它们,然后存储对那些长期存在的字符串的引用:

      let my_vec: Vec<u64> = vec![4, 3, 9];
      let labels: Vec<String> = (0..my_vec.len())
          .map(|i| i.to_string())
          .collect();
      
      let result: Vec<_> = labels.iter()
          .zip(my_vec)
          .map(|(k, v)| (k.as_str(), v))
          .collect();
      

      (Playground)

      这可能会使您的问题更上一层楼。您可能没有一个存储Strings 的好地方,这样它们的寿命就足够长了。如果您希望它们比当前函数调用更有效,它们不能位于堆栈上的局部变量中。

      第二种选择是将Strings 转换为Box&lt;str&gt;,然后将leak the memory 转换为Box::leak。泄露的引用永远存在,因此可以被视为'static

      注意不要滥用这种技术,只是在编译器抱怨生命周期时关闭它。只有当琴弦永远存在确实有意义时,您才应该这样做。如果标签将在您的应用程序运行的整个过程中显示,那很好。如果它们位于已关闭或以其他方式消失的 UI 元素中,请不要这样做。

      let my_vec: Vec<u64> = vec![4, 3, 9];
      
      let result: Vec<(&str, u64)> = my_vec.iter()
          .enumerate()
          .map(|(k, v)| (Box::leak(k.to_string().into_boxed_str()) as &str, *v))
          .collect();
      

      (Playground)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-30
        • 2021-03-16
        • 2022-01-20
        • 1970-01-01
        相关资源
        最近更新 更多