将Vec<u64>转化为Vec<(&str, u64)>,以供tui::BarChart数据使用。

17
我该如何将一个Vec<u64>转换成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)]);
}

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个回答

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)]);
}

虽然这在你的特定情况下可以工作,data()方法有点奇怪。不深入挖掘很难确定是否存在问题。在您所链接的示例中,str是静态的,也许它只是(或主要)用于像示例一样的用途,因此您不应该使用它来进行动态索引。


9
&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();

(游乐场)
这可能只是把你的问题升级了一个级别。你可能没有一个好的位置来存储 String,使它们足够长时间存在。如果你打算让它们生存超过当前函数调用,它们不能在栈上的局部变量中。
第二个选择是将 String 转换为 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();

(Playground)的介绍:

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接