I am trying to build objects for every element in a range n, so I at the end have a slice of objects.
Cannot seem to figure out the right way, though. Tried:
[0..n].map(convert_int_to_object)
Square brackets [] denote arrays in Rust; a..b or (a..b) denotes a range. So to create an iterator over the range, what you're looking for is:
(0..n).map(convert_int_to_object)
This creates an iterator, but you will need to collect all the new objects into a collection such as a Vec. For example:
fn my_fun(n: i32) -> Vec<MyStruct> {
(0..n).map(convert_int_to_object).collect()
}
You asked for a slice, but you are creating new objects (allocation) so you need to use a Vec because it owns the new objects. You can get a slice from a vector v with &v or v.as_slice().
let v = my_fun(10);
let slice = v.as_slice();
Related
I want to create a slice of numbers within certain range in Rust. However, the following syntax does not seem to work, as the output is a slice of length 1, composed of a Range object.
let count = 10;
let slice = [0..count/2];
What's the proper way to create a slice in Rust from a range?
You cannot create a slice directly from a range (unless you know an upper bound to the number of elements at compile time, in which case you can use an array), you have to use Vec:
let vec = Vec::from_iter(0..count/2);
let slice = vec.as_slice();
I would like to zip two vectors together, but what I get when calling the zip function is (&i32, &i32). I would like to get (i32, i32) - copy values from both vectors into a new vector.
let v1 = vec![1,2,3];
let v1 = vec![4,5,6];
// what I want
let zipped : Vec<(i32, i32)> = v1.iter().zip(v2.iter()).collect();
// what I actually get
let zipped : Vec<(&i32, &i32)> = v1.iter().zip(v2.iter()).collect();
Is it possible to force the zip function to copy the values?
zip() doesn't influence the values you're iterating over, it simply creates an iterator over tuples of the first and second iterator's values.
If you want to get owned values, you can use into_iter() on the Vecs. This will consume the vectors, so you can't use them anymore after the call. If you need to keep those vectors around, there's a copied() method that can be called on iterators over types that implement Copy, which is the case for i32. So you can get the same result while keeping the Vecs around by v1.iter().copied().zip(v2.iter().copied()).collect().
You can use cloned:
let zipped : Vec<(i32, i32)> = v1.iter().cloned().zip(v2.iter().cloned()).collect();
Playground
I'm new to rust and am trying to figure out how to create a HashMap of borrowed values from a Vec of data but when I try to do it I get a Vec into a HashMap the ownership model fights me. I don't know how to accomplish this, maybe I'm just trying something that is against the Rust mentality.
For Example:
struct Data{
id: String,
other_value: String,
}
//inside a method somewhere
let data_array = load_data(); // returns a Vec<Data>
let mut hash = HashMap::new(); // HashMap<&String, &Data>
for item in data_array {
hash.insert(&item.id, &item);
}
As far As I know there should be a way to populate this data in this way as the HashMap would be storing references to the original data. Or maybe I've just flat out misunderstood the docs... ¯_(ツ)_/¯
There key issue here is that you are consuming the Vec. for loops in Rust work over things that implement IntoIter. IntoIter moves the Vec into an iterator - the Vec itself no longer exists once this is done.
Therefore the items that you are looping though disappear at the end of each iteration., so those references end up referencing nonexistent data (dangling references). If you tried to using them, Bad Things Would Happen. Rust prevents you shooting yourself in the foot like that, so you get an error telling you that the reference does not live long enough. The solution to make your code compile is very easy. Just add .iter() to the end of the loop, which will iterate through references rather than consume the Vec.
for item in data_array.iter() {
hash.insert(&item.id, item); //Note we don't need an `&` in front of item
}
I'm still relatively new to Rust, so this might not be right, but I think it does what you want, but once and for all -- i.e. a function that makes it easy to turn a collection into a map using a closure to generate the keys:
fn map_by<I,K,V>(iterable: I, f: impl Fn(&V) -> K) -> HashMap<K,V>
where I: IntoIterator<Item = V>,
K: Eq + Hash
{
iterable.into_iter().map(|v| (f(&v), v)).collect()
}
Allowing you to say
map_by(data_array.iter(), |item| &item.id)
Here it is in the playground:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=87c0e4d1e68ccb6dd3f2c43ac9f318c7
Please nudge me in the right direction if I have this wrong.
Is there a function like this lying around in std?
So turns out you can borrow the iterator value by borrowing the collection (Vec). So the example above turns into:
for item in &data_array {
hash.insert(&item.id, item);
}
Notice the &data_array which turns item from Data type to &Data and allows you to use the borrowed value.
In the following rust code I am trying to change the contents of an array:
let mut example_state = [[0;8]; 2];
for mut i in example_state.iter() {
let mut k = 0;
for j in i.iter(){
i[k] = 9u8;
k +=1
}
}
However I get the error message:
src/main.rs:18:13: 18:23 error: cannot assign to immutable indexed content `i[..]`
src/main.rs:18 i[k] = 9u8;
which I'm confused by because I am defining i to be mut and example_state is also mutable.
I also don't know if this is the best way to change the contents of an array - do I need the counter k or can I simply use the iterator j in some way?
UPDATE:
So I found that this block of code works:
let mut example_state = [[n;8]; 2];
for i in example_state.iter_mut() {
for j in i.iter_mut(){
*j = 9u8;
}
}
but I would appreciate some explanation of what the difference is between them, iter_mut doesn't throw up much on Google.
Let's look at the signatures of the two methods, iter and iter_mut:
fn iter(&self) -> Iter<T>;
fn iter_mut(&mut self) -> IterMut<T>;
And the structs they return, Iter and IterMut, specifically the implementation of Iterator:
// Iter
type Item = &'a T
// IterMut
type Item = &'a mut T
These are associated types, but basically in this case, they specify what the return type of calling Iterator::next. When you used iter, even though it was on a mutable variable, you were asking for an iterator to immutable references to a type T (&T). That's why you weren't able to mutate them!
When you switched to iter_mut, the return type of Iterator::next is &mut T, a mutable reference to a type T. You are allowed to set these values!
As an aside, your question used arrays, not slices, but there aren't documentation links for arrays (that I could find quickly), and slices are close enough to arrays so I used them for this explanation.
There are two orthogonal concepts going on here:
Whether the reference itself is mutable. That's the difference between i and mut i.
Whether the data it points to is mutable. That's the difference between .iter()/&T and .iter_mut()/&mut T.
If you use C, this distinction should be familiar. Your initial code creates mutable references to immutable data, or const char * in C. So while you can assign to the reference itself (i = ...), you can't modify the data it points to (*i = ...). That's why the compiler stops you.
On the other hand, your fixed code creates immutable references to mutable data. That's char * const in C. This doesn't let you assign to the reference itself, but it does let you modify the underlying array, so it compiles as expected.
So why does Rust have a separate .iter() and .iter_mut()? Because in Rust, while you can take as many &T to a structure as you want, you can only modify it through a single &mut T. In other words, mutable references are unique and never alias.
Having both .iter() and .iter_mut() gives you a choice. On one hand, you can have any number of immutable iterators in scope at once, all pointing to the same array. Here's a silly example that iterates forwards and backwards at the same time:
for i, j in array.iter().zip(array.iter().rev()) {
println!("{} {}", i, j);
}
But if you want a mutable iterator, you have to guarantee the references never alias. So this won't work:
// Won't compile
for i, j in array.iter_mut().zip(array.iter_mut().rev()) {
println!("{} {}", i, j);
}
because the compiler can't guarantee i and j don't point to the same location in memory.
I'm having trouble combining two strings, I'm very new to rust so If there is an easier way to do this please feel free to show me.
My function loops through a vector of string tuples (String,String), what I want to do is be able to combine these two strings elements into one string. Here's what I have:
for tup in bmp.bitmap_picture.mut_iter() {
let &(ref x, ref y) = tup;
let res_string = x;
res_string.append(y.as_slice());
}
but I receive the error : error: cannot move out of dereference of '&'-pointer for the line: res_string.append(y.as_slice());
I also tried res_string.append(y.clone().as_slice()); but the exact same error happened, so I'm not sure if that was even right to do.
The function definition of append is:
fn append(self, second: &str) -> String
The plain self indicates by-value semantics. By-value moves the receiver into the method, unless the receiver implements Copy (which String does not). So you have to clone the x rather than the y.
If you want to move out of a vector, you have to use move_iter.
There are a few other improvements possible as well:
let string_pairs = vec![("Foo".to_string(),"Bar".to_string())];
// Option 1: leave original vector intact
let mut strings = Vec::new();
for &(ref x, ref y) in string_pairs.iter() {
let string = x.clone().append(y.as_slice());
strings.push(string);
}
// Option 2: consume original vector
let strings: Vec<String> = string_pairs.move_iter()
.map(|(x, y)| x.append(y.as_slice()))
.collect();
It seems like you might be confusing append, which takes the receiver by value and returns itself, with push_str, which simply mutates the receiver (passed by mutable reference) as you seem to expect. So the simplest fix to your example is to change append to push_str. You'll also need to change "ref x" to "ref mut x" so it can be mutated.