将 Vec<u64> 转为 Vec<(&str, u64)> 用于 tui::BarChart 数据

hob*_*mer 17 rust

我怎样才能把 aVec<u64>变成 a Vec<(&str, u64)>,使得前者的索引嵌入到str后者的部分中?

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

我想这样做的原因是因为我想使用来自 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)]);
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)

或使用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)]);
}
Run Code Online (Sandbox Code Playgroud)
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)>`
Run Code Online (Sandbox Code Playgroud)

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

当然,如果有更好的方法来绘制向量及其索引作为标签,我愿意接受建议。

Sta*_*eur 13

你不能直接做到这一点,&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)]);
}
Run Code Online (Sandbox Code Playgroud)

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


Joh*_*ica 8

&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();
Run Code Online (Sandbox Code Playgroud)

游乐场

这可能只会将您的问题提升一个层次。您可能没有一个很好的地方来存放Strings,这样它们就可以存活足够长的时间。如果您希望它们比当前函数调用更有效,它们就不能位于堆栈上的局部变量中。

第二个选择是将转换Strings到Box<str>,然后泄漏的存储器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();
Run Code Online (Sandbox Code Playgroud)

游乐场