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.
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!("
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 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.
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.
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.
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.
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.
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.
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 String
s 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 String
s 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 String
s 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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With