Why does Char's iterator removes element in Rust? [duplicate] - rust

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

Related

Why one Iter<String> yeilds String while another yields &String while iterating?

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).

Retrieve the state in scan() iterator?

To obtain the partial sums of a sequence of integers, I can use scan() on the iterator like this:
let partial: Box<[u32]> =
list
.iter()
.scan(0, |st, elem| {
let ret = *st;
*st += elem;
Some(ret)
})
.collect();
The above code works well, but I'm trying to modify it to give me the total sum as well.
Something like:
let (partial, total): (Box<[u32]>, u32) =
list
.iter()
.scan(0, |st, elem| {
// TODO
})
.collect();
It seems like I would just need to obtain the eventual value of st, and the iterator should already know it's value. However, I can't seem to find a way to retrieve that value, short of doing a second iteration over the whole sequence (e.g. with fold()).
Is there a way to find the partial sums and the total sum in a single pass?
Include the total sum in the scan, but then split off the last value.
use std::iter;
fn main() {
let list = vec![1, 2, 3, 4];
// Add zero at the start to emulate what you had before
let partial: Box<[u32]> = iter::once(0)
.chain(list.iter().scan(0, |st, elem| {
*st += elem;
Some(*st)
}))
.collect();
// unwrap since with the added zero, the slice will always be non-empty
let (total, partial) = partial.split_last().unwrap();
println!("partial sums: {:?}", partial);
println!("total sum: {}", total);
}
(playground)
Or using successors():
fn main() {
use std::iter::successors;
let list = vec![1, 2, 3, 4];
let mut iter = list.iter();
let partial: Vec<_> = successors(Some(0), |n| iter.next().map(|i| n + i)).collect();
// unwrap since with the added zero, the slice will always be non-empty
let (total, partial) = partial.split_last().unwrap();
assert_eq!(partial, &[0, 1, 3, 6]);
assert_eq!(total, &10);
}
(playground)
You must decide what you want to do in the closure.
As it stands in your code, you remember ret = *st which is the accumulator's value before the addition takes place, and you return Some(ret). Thus, the first item you get in the result is currently 0.
If you want the value after the sums, you should just return Some(*st), which is the updated accumulator's value after the addition.

Why does the closure executed in .map not change the captured value? [duplicate]

This question already has answers here:
How do I cope with lazy iterators?
(3 answers)
Closed 3 years ago.
Here is my code:
let mut v = Vec::new();
let _ = (0..5).map(|i| v.push(i));
println!("{:?}", v); //output: []
The captured value is v. I expect the code above to print [0, 1, 2, 3, 4], but it prints [].
Why is that?
The map method does not iterate through elements immediately. Instead, it creates a lazy iterator which you can use later. One of the ways to force the new iterator to perform its job is the Iterator::collect method. In your case, it will produce a new collection filled with empty values (() because it's the type of v.push(i)):
let mut v = Vec::new();
let v2: Vec<()> = (0..5).map(|i| v.push(i)).collect();
println!("{:?}", v); //output: [0, 1, 2, 3, 4]
This does excess work by creating the vector v2. Try to avoid such inefficient operations.

How do I concatenate two slices in Rust?

I want to take the x first and last elements from a vector and concatenate them. I have the following code:
fn main() {
let v = (0u64 .. 10).collect::<Vec<_>>();
let l = v.len();
vec![v.iter().take(3), v.iter().skip(l-3)];
}
This gives me the error
error[E0308]: mismatched types
--> <anon>:4:28
|
4 | vec![v.iter().take(3), v.iter().skip(l-3)];
| ^^^^^^^^^^^^^^^^^^ expected struct `std::iter::Take`, found struct `std::iter::Skip`
<anon>:4:5: 4:48 note: in this expansion of vec! (defined in <std macros>)
|
= note: expected type `std::iter::Take<std::slice::Iter<'_, u64>>`
= note: found type `std::iter::Skip<std::slice::Iter<'_, u64>>`
How do I get my vec of 1, 2, 3, 8, 9, 10? I am using Rust 1.12.
Just use .concat() on a slice of slices:
fn main() {
let v = (0u64 .. 10).collect::<Vec<_>>();
let l = v.len();
let first_and_last = [&v[..3], &v[l - 3..]].concat();
println!("{:?}", first_and_last);
// The output is `[0, 1, 2, 7, 8, 9]`
}
This creates a new vector, and it works with arbitrary number of slices.
(Playground link)
Ok, first of all, your initial sequence definition is wrong. You say you want 1, 2, 3, 8, 9, 10 as output, so it should look like:
let v = (1u64 .. 11).collect::<Vec<_>>();
Next, you say you want to concatenate slices, so let's actually use slices:
let head = &v[..3];
let tail = &v[l-3..];
At this point, it's really down to which approach you like the most. You can turn those slices into iterators, chain, then collect...
let v2: Vec<_> = head.iter().chain(tail.iter()).collect();
...or make a vec and extend it with the slices directly...
let mut v3 = vec![];
v3.extend_from_slice(head);
v3.extend_from_slice(tail);
...or extend using more general iterators (which will become equivalent in the future with specialisation, but I don't believe it's as efficient just yet)...
let mut v4: Vec<u64> = vec![];
v4.extend(head);
v4.extend(tail);
...or you could use Vec::with_capacity and push in a loop, or do the chained iterator thing, but using extend... but I have to stop at some point.
Full example code:
fn main() {
let v = (1u64 .. 11).collect::<Vec<_>>();
let l = v.len();
let head = &v[..3];
let tail = &v[l-3..];
println!("head: {:?}", head);
println!("tail: {:?}", tail);
let v2: Vec<_> = head.iter().chain(tail.iter()).collect();
println!("v2: {:?}", v2);
let mut v3 = vec![];
v3.extend_from_slice(head);
v3.extend_from_slice(tail);
println!("v3: {:?}", v3);
// Explicit type to help inference.
let mut v4: Vec<u64> = vec![];
v4.extend(head);
v4.extend(tail);
println!("v4: {:?}", v4);
}
You should collect() the results of the take() and extend() them with the collect()ed results of skip():
let mut p1 = v.iter().take(3).collect::<Vec<_>>();
let p2 = v.iter().skip(l-3);
p1.extend(p2);
println!("{:?}", p1);
Edit: as Neikos said, you don't even need to collect the result of skip(), since extend() accepts arguments implementing IntoIterator (which Skip does, as it is an Iterator).
Edit 2: your numbers are a bit off, though; in order to get 1, 2, 3, 8, 9, 10 you should declare v as follows:
let v = (1u64 .. 11).collect::<Vec<_>>();
Since the Range is left-closed and right-open.

How do I get the first character out of a string?

I want to get the first character of a std::str. The method char_at() is currently unstable, as is String::slice_chars.
I have come up with the following, but it seems excessive to get a single character and not use the rest of the vector:
let text = "hello world!";
let char_vec: Vec<char> = text.chars().collect();
let ch = char_vec[0];
UTF-8 does not define what "character" is so it depends on what you want. In this case, chars are Unicode scalar values, and so the first char of a &str is going to be between one and four bytes.
If you want just the first char, then don't collect into a Vec<char>, just use the iterator:
let text = "hello world!";
let ch = text.chars().next().unwrap();
Alternatively, you can use the iterator's nth method:
let ch = text.chars().nth(0).unwrap();
Bear in mind that elements preceding the index passed to nth will be consumed from the iterator.
I wrote a function that returns the head of a &str and the rest:
fn car_cdr(s: &str) -> (&str, &str) {
for i in 1..5 {
let r = s.get(0..i);
match r {
Some(x) => return (x, &s[i..]),
None => (),
}
}
(&s[0..0], s)
}
Use it like this:
let (first_char, remainder) = car_cdr("test");
println!("first char: {}\nremainder: {}", first_char, remainder);
The output looks like:
first char: t
remainder: est
It works fine with chars that are more than 1 byte.
Get the first single character out of a string w/o using the rest of that string:
let text = "hello world!";
let ch = text.chars().take(1).last().unwrap();
It would be nice to have something similar to Haskell's head function and tail function for such cases.
I wrote this function to act like head and tail together (doesn't match exact implementation)
pub fn head_tail<T: Iterator, O: FromIterator<<T>::Item>>(iter: &mut T) -> (Option<<T>::Item>, O) {
(iter.next(), iter.collect::<O>())
}
Usage:
// works with Vec<i32>
let mut val = vec![1, 2, 3].into_iter();
println!("{:?}", head_tail::<_, Vec<i32>>(&mut val));
// works with chars in two ways
let mut val = "thanks! bedroom builds YT".chars();
println!("{:?}", head_tail::<_, String>(&mut val));
// calling the function with Vec<char>
let mut val = "thanks! bedroom builds YT".chars();
println!("{:?}", head_tail::<_, Vec<char>>(&mut val));
NOTE: The head_tail function doesn't panic! if the iterator is empty. If this matched Haskell's head/tail output, this would have thrown an exception if the iterator was empty. It might also be good to use iterable trait to be more compatible to other types.
If you only want to test for it, you can use starts_with():
"rust".starts_with('r')
"rust".starts_with(|c| c == 'r')
I think it is pretty straight forward
let text = "hello world!";
let c: char = text.chars().next().unwrap();
next() takes the next item from the iterator
To “unwrap” something in Rust is to say, “Give me the result of the computation, and if there was an error, panic and stop the program.”
The accepted answer is a bit ugly!
let text = "hello world!";
let ch = &text[0..1]; // this returns "h"

Resources