将Vec <u64>转换为Vec <(&str,u64)>以获取tui :: BarChart数据

时间:2020-05-23 21:52:47

标签: rust

如何将Vec<u64>变成Vec<(&str, u64)>,例如 前者的索引已嵌入后者的str部分中?

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

我要这样做的原因是因为我想绘制我的条形图 使用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()的寿命不足。

当然,如果有更好的方法来获取矢量,我愿意提出建议 以其索引作为标签进行绘制。

2 个答案:

答案 0 :(得分:13)

您不能直接这样做,&str借用字符串,因此在借用字符串时字符串必须保持活动状态,通常的答案是创建,存储和借用字符串,例如:< / p>

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是静态的,也许仅(或主要)打算像示例一样使用,因此不希望将其与动态索引一起使用。

答案 1 :(得分:8)

&str引用必须指向比result更长的字符串。您不能使用对临时字符串的引用,因为它们被丢弃得太早了。

一个选择是将所有String存储在一个寿命超过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

那可能会使您的问题更上一层楼。您可能没有一个存放String的地方,因为它们的寿命足够长。如果您希望它们在当前函数调用之前不存在,则它们不能位于堆栈的局部变量中。

第二种选择是将String转换为Box<str>,然后将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