I am playing around with Rust trying to get a better understanding of how the reference works.
I have below code:
let owned_arr: [String; 2] = ["hello".to_string(), "world".to_string()];
let ref_arra: &[String; 2] = &["hello".to_string(), "world".to_string()];
Then I get the iterator as follows:
let owned_arr: [String; 2] = ["hello".to_string(), "world".to_string()];
let ref_arra: &[String; 2] = &["hello".to_string(), "world".to_string()];
let owned_iter: Iter<String> = owned_arr.iter();
let ref_iter: Iter<String> = ref_arr.iter();
I noticed that both has type Iter<String>.
Then instead of getting the iterator like this, I decided to use the for ... in syntax:
let owned_arr: [String; 2] = ["hello".to_string(), "world".to_string()];
let ref_arra: &[String; 2] = &["hello".to_string(), "world".to_string()];
// let owned_iter: Iter<String> = owned_arr.iter();
// let ref_iter: Iter<String> = ref_arr.iter();
for i in owned_arr {
let j: String = i;
dbg!(j);
}
for i in ref_arr {
let j: &String = i;
dbg!(j);
}
My question is, why is it that with owned_arr: [String; 2] the item retrieved in the for .. in is String, but for &[String; 2] the item retrieved is &String even though when the iterator of both is retrieved it comes out as Iter<String>. Why the difference then, when the iterator us used in a for..in?
First of all Iter<String> is really std::slice::Iter<'_, String>. And it will return references to Strings, not owned Strings.
Secondly for loops use IntoIterator trait (see doc). Owned array returns a std::array::IntoIter as it's iterator, but a reference to the array returns std::slice::Iter. First one returns owned strings (as it consumes array) and the second one returns references to strings (as it borrows array).
Related
In my understanding, s[0..] calls the method ops::Index::index. I try to call s.index(0..), but find that it returns a type of &str. The code is as follows
let a = String::from("hello world");
let b = &a;
let c = b.index(1..);
println!("{}", c);
let d = b[1..];
println!("{}", d);
This code will display an error, and indicate that the variable d is of type str.
What do I understand wrong? and how to understand the [] and index() in rust?
In the documentation of std::ops::Index, we find:
container[index] is actually syntactic sugar for *container.index(index)
and
This allows nice things such as let value = v[index] if the type of value implements Copy.
In your example, c has type &str as you expect, but d would be the dereference (*) of such an &str; this would lead to str which is rejected by the compiler (not sized).
In order to obtain a &str in d, you could write:
let d = &b[1..]; // & was missing
which is really explicit because it clearly states: «I want to refer to something which stands inside b».
Here is another example of the implicit dereference (*) in a different context:
let mut arr = [1, 2, 3, 4];
let n = arr[1]; // not *arr[1]
arr[2] += n; // not *arr[2]
println!("{:?}", arr); // [1, 2, 5, 4]
This question already has answers here:
What is the difference between iter and into_iter?
(5 answers)
Explanation of objects moves and borrowings int rev(), chars() and next()
(2 answers)
Closed 7 months ago.
I stumbled on something I was not expecting when using the Chars type. I called, the .next() method and found out it was mutating the original char values.
To illustrate in code and to compare with calling next on a vec:
let mut c: Chars = "hello".chars();
dbg!(c.next()); // prints Some("h")
dbg!(c); // prints Chars(['e','l','l','o',])
As you can see, after calling next the h is gone, and the c value is now 'e','l','l','o'
Whereas with vec, that is not the case:
let v1 = vec![1, 2, 3];
let mut v1_iter = v1.iter();
dbg!(v1_iter.next()); // prints Some(1)
dbg!(v1); // prints [1,2,3]
As can be seen, calling next does not mutate v1 to remove the 1 element.
Why is this the case with Chars? Is it demonstrating a well defined characteristics for some type of iterators in rust that I am not aware of? That it iterators where iterating actually consumes and mutates the original value being iterated?
You're comparing apples and oranges here. In one case you're printing the iterator itself, while in the other you're printing the original container. If you print v1_iter, or if you do let s = "hello"; let c = s.chars(); and print s, you'll notice that they behave the same:
let s = "hello";
let mut c: Chars = s.chars();
dbg!(c.next()); // prints Some("h")
dbg!(c); // prints Chars(['e','l','l','o',])
dbg!(s); // prints "hello"
let v1 = vec![1, 2, 3];
let mut v1_iter = v1.iter();
dbg!(v1_iter.next()); // prints Some(1)
dbg!(v1_iter); // prints Iter ([ 2, 3, ],)
dbg!(v1); // prints [1,2,3]
Playground
How can I make a Vec containing [0,1,2,3,4,3,2,1,0] using ranges?
I tried this:
let mut a = vec![0..len];
let b = vec![len..=0];
a.extend(&b);
But it raises an error:
type mismatch resolving `<&Vec<RangeInclusive<usize>> as IntoIterator>::Item == std::ops::Range<usize>`
There's no direct syntax that I know of to create a Vec directly from a range. A range is an first-class object and implements Iterator, so you can collect a range into a Vec.
Ranges must also be from low to high. To get a reversed range create a regular forward range and call rev to create an iterator that iterates in reverse.
let mut a = (0..len).collect::<Vec<_>>();
let b = (0..=len).rev();
a.extend(&b);
You'll notice that collect requires ::<>, the so-called turbofish operator. That's because it's a generic method that can convert an iterator into any type that implements FromIterator. There are many collection types that implement FromIterator, so we must tell it we want a Vec in particular.
Declaring a's type would have also worked:
let mut a: Vec<_> = (0..len).collect();
let b = (0..=len).rev();
a.extend(&b);
It's also possible to construct the vector in place by chaining the two iterators together and then collecting the result. This way a doesn't need to be mut.
let a = (0..len).chain((0..=len).rev())
.collect::<Vec<_>>();
fn main() {
let len = 4;
let mut a = (0..len).collect::<Vec<_>>();
let b = (0..=len).rev();
a.extend(b);
println!("{:?}", a); // should be [0, 1, 2, 3, 4, 3, 2, 1, 0]
}
I have this toy example, but it's what I'm trying to accomplish:
fn lazy_vec() {
let vec: Vec<i64> = vec![1, 2, 3, 4, 5];
let mut iter: Box<Iterator<Item = i64>> = Box::new(vec.into_iter());
iter = Box::new(iter.map(|x| x + 1));
// potentially do additional similar transformations to iter
println!("{:?}", iter.collect::<Vec<_>>());
}
This (if I'm not mistaken) is a lazy iterator pattern, and the actual map operation doesn't occur until .collect() is called. I want to do the same thing with slices:
fn lazy_slice() {
let vec: Vec<i64> = vec![1, 2, 3, 4, 5];
let slice: &[i64] = &vec[..3];
let mut iter: Box<Iterator<Item = i64>> = Box::new(slice.into_iter());
iter = Box::new(iter.map(|x| x + 1));
// potentially do additional similar transformations to iter
println!("{:?}", iter.collect::<Vec<_>>());
}
This results in a type mismatch:
error[E0271]: type mismatch resolving `<std::slice::Iter<'_, i64> as std::iter::Iterator>::Item == i64`
--> src/main.rs:4:47
|
4 | let mut iter: Box<Iterator<Item = i64>> = Box::new(slice.into_iter());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found i64
|
= note: expected type `&i64`
found type `i64`
= note: required for the cast to the object type `std::iter::Iterator<Item=i64>`
I can't figure out what I need to do to resolve this error. The second note made me think I needed:
iter = Box::new(iter.map(|x| x + 1) as Iterator<Item = i64>);
or
iter = Box::new(iter.map(|x| x + 1)) as Box<Iterator<Item = i64>>;
These fail with other errors depending on the exact syntax (e.g. expected reference, found i64, or expected i64, found &i64). I've tried other ways to declare the types involved, but I'm basically just blindly adding & and * in places and not making any progress.
What am I missing here? What do I need to change in order to make this compile?
Edit
Here's a slightly more concrete example - I need iter to be mut so that I can compose an unknown number of such transformations before actually invoking .collect(). My impression was this was a somewhat common pattern, apologies if that wasn't correct.
fn lazy_vec(n: i64) {
let vec: Vec<i64> = vec![1, 2, 3, 4, 5];
let mut iter: Box<Iterator<Item = i64>> = Box::new(vec.into_iter());
for _ in 0..n {
iter = Box::new(iter.map(|x| x + 1));
}
println!("{:?}", iter.collect::<Vec<_>>());
}
I'm aware I could rewrite this specific task in a simpler way (e.g. a single map that adds n to each element) - it's an oversimplified MCVE of the problem I'm running into. My issue is this works for lazy_vec, but I'm not sure how to do the same with slices.
Edit 2
I'm just learning Rust and some of the nomenclature and concepts are new to me. Here's what I'm envisioning doing in Python, for comparison. My intent is to do the same thing with slices that I can currently do with vectors.
#!/usr/bin/env python3
import itertools
ls = [i for i in range(10)]
def lazy_work(input):
for i in range(10):
input = (i + 1 for i in input)
# at this point no actual work has been done
return input
print("From list: %s" % list(lazy_work(ls)))
print("From slice: %s" % list(lazy_work(itertools.islice(ls, 5))))
Obviously in Python there's no issues with typing, but hopefully that more clearly demonstrates my intent?
As discussed in What is the difference between iter and into_iter?, these methods create iterators which yield different types when called on a Vec compared to a slice.
[T]::iter and [T]::into_iter both return an iterator which yields values of type &T. That means that the returned value doesn't implement Iterator<Item = i64> but instead Iterator<Item = &i64>, as the error message states.
However, your subsequent map statements change the type of the iterator's item to an i64, which means the type of the iterator would also need to change. As an analogy, you've essentially attempted this:
let mut a: &i64 = &42;
a = 99;
Iterator::cloned exists to make clones of the iterated value. In this case, it converts a &i64 to an i64 essentially dereferencing the value:
fn lazy_slice(n: i64) {
let array = [1i64, 2, 3, 4, 5];
let mut iter: Box<Iterator<Item = i64>> = Box::new(array.iter().cloned());
for _ in 0..n {
iter = Box::new(iter.map(|x| x + 1));
}
println!("{:?}", iter.collect::<Vec<_>>());
}
Editor's note: this question was asked before Rust 1.0 and some of the assertions in the question are not necessarily true in Rust 1.0. Some answers have been updated to address both versions.
I want to create a vector, but I only know the size I want the vector to be at runtime. This is how I'm doing it now (i.e. creating an empty, mutable vector, and adding vectors to it) :
fn add_pairs(pairs: ~[int]) -> ~[int] {
let mut result : ~[int] = ~[];
let mut i = 0;
while i < pairs.len() {
result += ~[pairs[i] + pairs[i + 1]];
i += 2;
}
return result;
}
This is how I want to do it (i.e., creating a vector and putting everything in it, instead of adding lots of vectors together):
fn add_pairs(pairs: ~[int]) -> ~[int] {
let number_of_pairs = pairs.len() / 2;
let result : ~[int, ..number_of_pairs];
let mut i = 0;
while i < pairs.len() {
result[i] = pairs[2 * i] + pairs[2 * i + 1];
i += 1;
}
return result;
}
Unfortunately, doing the above gives me something like:
error: expected constant expr for vector length: Non-constant path in constant expr
let result: ~[int, ..number_of_pairs];
^~~~~~~~~~~~~~~~~~~~~~~~
I get the impression that vectors have to have their size known at compile time (and so you need to set their size to a constant). Coming from a Java background, I'm confused! Is there a way to create a vector whose size you only know at runtime?
I'm using Rust 0.6.
In Rust version 1.0.0, they've made the std::vec:Vec public structure stable so that you can instantiate a growable vector with let mut my_vec = Vec::new(); You can also use the vec! macro like so: let mut another_vec = vec![1isize, 2isize, 3isize]; What is important to note is that in both cases the variable you're assigning must be mutable.
With these vectors you can call my_vec.push(num); for individual items or another_vec.extend_from_slice(["list", "of", "objects"]); to add items to the end of the vector.
For your specific problem, you could do something like this:
fn add_pairs(pairs: Vec<(Vec<isize>)>) -> Vec<isize> {
let mut result = Vec::new();
for pair in pairs.iter() {
result.push(pair[0]);
result.push(pair[1]);
}
return result;
}
You can see this in action on the Rust Playground where you have (what I assumed) was a nested vector of integer pairs.
There is no way to create an array of constant length with the length determined at runtime; only compile-time constant length arrays are allowed, so (variations of) your first method with Vec<i32> (previously ~[int]) is the only supported way. You could use vec![0; number_of_pairs] to create a vector of the correct size and use the second part.
There are many helper functions for what you are trying to do (using while directly Rust should be very rare):
fn add_pairs(pairs: &[i32]) -> Vec<i32> {
let mut result = Vec::new();
for i in 0..(pairs.len() / 2) {
result.push(pairs[2 * i] + pairs[2 * i + 1])
}
result
}
Or even
fn add_pairs(pairs: &[i32]) -> Vec<i32> {
pairs
.chunks(2)
.filter(|x| x.len() == 2)
.map(|x| x[0] + x[1])
.collect()
}
Docs: chunks, filter, map, collect. (The filter is just because the last element of chunks may have length 1.)
Also note that adding two vectors allocates a whole new one, while push doesn't do this necessarily and is much faster (and .collect is similar).
In at least Rust 1.0, there is a Vec::with_capacity() function that handles this scenario.
Example code:
let n = 44; // pretend this is determined at run time
let mut v = Vec::<f64>::with_capacity(n);
v.push(6.26);
println!("{:?}", v); // prints [6.26]
println!("{:?}", v.len()); // prints 1
println!("{:?}", v.capacity()); // prints 44