How do I concatenate two slices in Rust? - 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.

Related

Consume and replace elements in a vector with 1 or more elements

I'm trying to figure out an efficient Rust way of consuming elements of a vector and replacing them with one or more elements (of the same type) like in the non-compiling example:
fn main() {
let mut myvec = vec![0, 1];
println!("{:?}", myvec);
for val in myvec.drain(..) {
myvec.push(val+2);
myvec.push(val+4);
}
println!("{:?}", myvec);
}
However, I am not even sure if Rust would even allow that as each operation needs a mutable reference (i.e. we need 2 mutable refs) but Rust can only allow one. Is there a way doing what I want to do or do I just need to have 2 separate vectors:
let mut myvec = vec![0, 1];
let mut newvec = Vec::new();
println!("{:?}", myvec);
for val in myvec.drain(..) {
newvec.push(val+2);
newvec.push(val+4);
}
println!("{:?}", newvec);
which outputs:
[0, 1]
[2, 4, 3, 5]
PS. I know that the splice method can achieve what I need but I also need a mutable reference inside the for loop.
Since you are changing the size of the Vec, it's likely that it will need reallocation anyway. Additionally, you cannot mutate a Vec while you are iterating it (precisely because mutating it could cause it to reallocate). It won't be much different to collect into a new Vec:
myvec = myvec
.drain(..)
.flat_map(|val| iter::once(val + 2).chain(iter::once(val + 4)))
.collect();
The chained iterator might not be as optimal as it could be. In nightly Rust, you could do:
#![feature(array_value_iter)]
use std::array;
myvec = myvec
.drain(..)
.flat_map(|val| array::IntoIter::new([val + 2, val + 4]))
.collect();
Which should be more efficient.
You could swap elements of the same type directly in memory:
use core::mem::swap;
fn main() {
let mut myvec = vec![0, 1];
let mut result = Vec::new();
println!("{:?}", myvec);
for i in (0..myvec.len()).step_by(2) {
let mut x = myvec[i] + 2;
swap(&mut myvec[i], &mut x);
result.push(x);
if i + 1 < myvec.len() {
let mut y = myvec[i + 1] + 4;
swap(&mut myvec[i + 1], &mut y);
result.push(y);
}
}
println!("{:?} {:?}", myvec, result);
}
After posting I realized that you require replacing an element with more than one element, and this code won't do that. I'll leave it up in case it helps, though.

How do I avoid allocations in Iterator::flat_map?

I have a Vec of integers and I want to create a new Vec which contains those integers and squares of those integers. I could do this imperatively:
let v = vec![1, 2, 3];
let mut new_v = Vec::new(); // new instead of with_capacity for simplicity sake.
for &x in v.iter() {
new_v.push(x);
new_v.push(x * x);
}
println!("{:?}", new_v);
but I want to use iterators. I came up with this code:
let v = vec![1, 2, 3];
let new_v: Vec<_> = v.iter()
.flat_map(|&x| vec![x, x * x])
.collect();
println!("{:?}", new_v);
but it allocates an intermediate Vec in the flat_map function.
How to use flat_map without allocations?
As of Rust 1.53.0, this can be written with just the array literal:
let v = vec![1, 2, 3];
let new_v: Vec<_> = v.iter()
.flat_map(|&x| [x, x * x])
.collect();
Rust 1.53.0 implements IntoIterator for arrays, so the vec![] and workarounds in previous solutions are no longer needed. This works on all editions.
If your iterator is small and you don't want any external dependencies, a short iterator can be constructed from std::iter::once and std::iter::Iterator::chain. For example,
use std::iter;
let v = vec![1, 2, 3];
let new_v: Vec<_> = v
.iter()
.flat_map(|&x| iter::once(x).chain(iter::once(x * x)))
.collect();
println!("{:?}", new_v);
(playground)
This could be made into a macro, though be aware that using this for too many elements may cause the recursion limit to be reached. If you're making an iterator for more than a few dozen elements, it's probably not too bad to have an allocation. If you really need the slight increase in performance, nnnmmm's solution is probably better.
macro_rules! small_iter {
() => { std::iter::empty() };
($x: expr) => {
std::iter::once($x)
};
($x: expr, $($y: tt)*) => {
std::iter::once($x).chain(small_iter!($($y)*))
};
}
fn main() {
let v = vec![1, 2, 3];
let new_v: Vec<_> = v
.iter()
.flat_map(|&x| small_iter!(x, x * x))
.collect();
println!("{:?}", new_v);
}
(playground)
As of version 1.51.0, the struct core::array::IntoIter has been stabilized. You can use it like this:
use core::array;
let v = vec![1, 2, 3];
let new_v: Vec<_> = v.iter()
.flat_map(|&x| array::IntoIter::new([x, x * x]))
.collect();
The documentation warns that this may be deprecated in the future when IntoIterator is implemented for arrays, but currently it's the easiest way to do this.
You can use an ArrayVec for this.
let v = vec![1, 2, 3];
let new_v: Vec<_> = v.iter()
.flat_map(|&x| ArrayVec::from([x, x * x]))
.collect();
Making arrays be by-value iterators, so that you wouldn't need ArrayVec has been discussed, see https://github.com/rust-lang/rust/issues/25725 and the linked PRs.

What type signature to use for an iterator generated from a slice?

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<_>>());
}

How can I introduce a copied variable as mutable in a if-let statement?

I have a HashMap<i8, i8> which could contain cycles:
let mut x: HashMap<i8, i8> = HashMap::new();
x.insert(1, 6);
x.insert(3, 5);
x.insert(5, 1);
To get the final value for 3, it should first lookup x[3], then x[5] and finally x[1] which should yield 6. I decided to use a while let loop:
let mut y = x[&3]; // y: i8
while let Some(&z) = x.get(&y) {
y = z;
}
println!("{}", y);
x.insert(0, 0);
This works fine, but it would panic! if 3 is not in the map. As I don't want to do anything about the None case, I want to use a if let (similar to the while let used).
I have tried some notations:
if let Some(&y) = x.get(&3): copies the value, but y is immutable (y: i8)
if let Some(mut y) = x.get(&3): y is mutable, but the value is borrowed (mut y: &i8)
if let mut Some(&y) = x.get(&3): my target: mutable copy, but invalid syntax (mut y: i8)
(All variants are available at Rust Playground, but you need to comment out the third try, as it is invalid syntax)
I would not argue about the second variant, but I need to insert values into my map in the body of the if let. As the map remains borrowed, I can't insert anymore. All I would need is that the value in Some(y) is copied, and y is mutable, so that the borrow checker is satisfied and I can do my recursive lookups.
Your approach #1 is a perfectly correct match, you just need to make the y variable mutable. One possibility is to convert Option<&i8> to Option<i8>, enabling the use of mut y in the pattern. For example, Option::map can dereference the value:
if let Some(mut y) = x.get(&3).map(|ref| *ref) {
Since Copy implies (cheap) Clone, you can express the same using Option::cloned():
if let Some(mut y) = x.get(&3).cloned() {
As of Rust 1.35, you can use Option::copied(), which is only defined for Copy types and just copies the value:
if let Some(mut y) = x.get(&3).copied() {
Another possibility is to leave your approach #1 as-is, but correct it simply by introducing a separate mutable variable inside the if let block:
if let Some(&y) = x.get(&3) {
let mut y = y;
...
Your code basically works:
use std::collections::HashMap;
fn main() {
let mut x: HashMap<i8, i8> = HashMap::new();
x.insert(1, 6);
x.insert(3, 5);
x.insert(5, 1);
let mut key = 3;
while let Some(&z) = x.get(&key) {
key = z;
}
println!("{}", key);
x.insert(key, 0);
}
Here, key is left as the last key that did not match.

Lifetime trouble when slice filtering

I want to optionally filter a slice without changing the type. The following approach fails:
use std::collections::HashSet;
fn main() {
let include: Option<HashSet<i8>> = Some(HashSet::new());
let y: &[i8] = &[1, 2, 3];
let z: &[i8] = match include {
Some(set) => {
let filtered: Vec<i8> = y.iter().filter(|&i| set.contains(i)).map(|&i| i).collect();
filtered.as_slice()
},
None => y
};
println!("{:?}", z);
}
with error
error: `filtered` does not live long enough
--> <anon>:10:9
|
9 | filtered.as_slice()
| -------- borrow occurs here
10 | },
| ^ `filtered` dropped here while still borrowed
...
14 | }
| - borrowed value needs to live until here
-- playground
I understand that filtered doesn't live long enough because the slice is simply a reference into it. I have tried to copy or clone or otherwise make a static version of the resulting filtered.as_slice(), but nothing compiles.
Declaring filtered outside of the definition of z won't work, because it requires set to do the filtering.
I expect this dance of converting to an iterator, then to a vector, and then to a slice is probably not idiomatic. So I'm interested to know of a better approach.
Declaring filtered outside of the definition of z won't work, because it requires set to do the filtering.
I don't understand why not. In the statement filtered.as_slice(), you borrow from filtered, so you must make sure filtered lives longer than the variable you assign it to (z):
use std::collections::HashSet;
fn main() {
let mut h = HashSet::new();
h.insert(2);
let exclude: Option<HashSet<i8>> = Some(h);
let y: &[i8] = &[1, 2, 3];
let filtered: Vec<i8>;
let z: &[i8] = match exclude {
Some(set) => {
filtered = y.iter().filter(|&i| !set.contains(i)).map(|&i| i).collect();
filtered.as_slice()
},
None => y
};
println!("{:?}", z); // outputs [1, 3]
}
Note I inverted the logic in filter.

Resources