Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Turn Vec<u64> into Vec<(&str, u64)> for tui::BarChart data

Tags:

rust

How can I turn a Vec<u64> into a Vec<(&str, u64)>, such that the index of the former is embedded into the str part of the latter?

For example, [4, 9, 3] should be turned into [("0", 4), ("1", 9), ("2", 3)].

The reason I want to do this is because I want to plot a barchart of my vector using the barchart from TUI, which requires a type like that.

I've tried some obvious things such as looping and pushing:

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

or using 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)>`

But whatever I do I just can't seem to get around the lifetime issue, as k.to_string() doesn't live long enough.

Of course, I'm open to suggestions if there are better ways to get the vector plotted with its indices as labels.

like image 678
hobbyprogrammer Avatar asked May 23 '20 21:05

hobbyprogrammer


People also ask

What is VEC rust?

The Vec type allows to access values by index, because it implements the Index trait. An example will be more explicit: let v = vec![ 0, 2, 4, 6]; println!("

How do you initialize VEC in Rust?

In Rust, there are several ways to initialize a vector. In order to initialize a vector via the new() method call, we use the double colon operator: let mut vec = Vec::new();

How do you access vector elements in Rust?

How to access vector values. Rust provides us with two methods to access elements in a vector. We can access values with either the indexer, or with the get() method.

How do I write a VEC<U8> vector?

If you’re fine with just getting a prefix of the Vec<T> , you can call .truncate (N) first. Write is implemented for Vec<u8> by appending to the vector.

What is VEC in C++?

A contiguous growable array type, written as Vec<T> and pronounced ‘vector’. The vec! macro is provided to make initialization more convenient: It can also initialize each element of a Vec<T> with a given value.

How do you truncate a value in a VEC?

The return values from f will end up in the Vec in the order they have been generated. If new_len is less than len, the Vec is simply truncated. This method uses a closure to create new values on every push. If you’d rather Clone a given value, use Vec::resize.

How to convert a VEC<T> to a box<[T]> without moving the elements?

If len == capacity , (as is the case for the vec! macro), then a Vec<T> can be converted to and from a Box< [T]> without reallocating or moving the elements. Vec will not specifically overwrite any data that is removed from it, but also won’t specifically preserve it.


2 Answers

You can't do it directly, &str borrow the string so the string must stay alive while you borrow them, the general answer is to create your string, stock them and borrow them, like for example:

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

While this will work on your specific case data() is strange. Without digging more it's hard to tell if there is something wrong. In the example you linked, the str are static maybe it's only(or mainly) intended to be use like the example, and so you are not expected to use it with dynamic index.

like image 164
Stargateur Avatar answered Oct 24 '22 15:10

Stargateur


The &str references have to point to strings that will outlive result. You can't use references to temporary strings as they're dropped too soon.

One option is to store all the Strings in a collection that outlives result. Precompute them and then store references to those long-lived strings:

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)

That may just push your problem up a level. You may not have a good place to store Strings such that they live long enough. They can't be in a local variable on the stack if you intend them to outlive the current function call.

A second option is to convert the Strings to Box<str> and then leak the memory with Box::leak. Leaked references live forever and therefore can be treated as 'static.

Be careful not to abuse this technique just to shut the compiler up when it complains about lifetimes. You should only do it if it really does make sense for the strings to live forever. If the labels will be displayed the entire time your application runs then it's fine. Don't do it if they'll be in a UI element that is closed or otherwise disappears.

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)

like image 30
John Kugelman Avatar answered Oct 24 '22 14:10

John Kugelman