Does a '&&x' pattern match cause x to be copied? - rust

In the documentation for std::iter::Iterator::filter() it explains that values are passed to the closure by reference, and since many iterators produce references, in that case the values passed are references to references. It offers some advice to improve ergonomics, by using a &x pattern to remove one level of indirection, or a &&x pattern to remove two levels of indirection.
However, I've found that this second pattern does not compile if the item being iterated does not implement Copy:
#[derive(PartialEq)]
struct Foo(i32);
fn main() {
let a = [Foo(0), Foo(1), Foo(2)];
// This works
let _ = a.iter().filter(|&x| *x != Foo(1));
// This also works
let _ = a.iter().filter(|&x| x != &Foo(1));
// This does not compile
let _ = a.iter().filter(|&&x| x != Foo(1));
}
The error you get is:
error[E0507]: cannot move out of a shared reference
--> src/main.rs:14:30
|
14 | let _ = a.iter().filter(|&&x| x != Foo(1));
| ^^-
| | |
| | data moved here
| | move occurs because `x` has type `Foo`, which does not implement the `Copy` trait
| help: consider removing the `&`: `&x`
Does this mean that if I use the &&x destructuring pattern, and the value is Copy, Rust will silently copy every value I am iterating over? If so, why does that happen?

In Rust, function or closure arguments are irrefutable patterns.
In the Rust reference, it says:
By default, identifier patterns bind a variable to a copy of or move
from the matched value depending on whether the matched value
implements Copy.
So, in this case:
let _ = a.iter().filter(|&x| *x != Foo(1));
the closure is being passed a reference to a reference to the item being iterated; therefore x is bound to a copy of a reference to the item. You can always copy a reference (it is basically a no-op) so this always succeeds.
In this case:
let _ = a.iter().filter(|&&x| x != Foo(1));
x is being bound to a copy of the item itself - which fails if the item is not Copy.
The reference also says:
This can be changed to bind to a reference by using the ref keyword,
or to a mutable reference using ref mut.
That's not so useful in this case though: &&ref x results in x being a reference to the item, the same as if you had used &x.

Does this mean that if I use the &&x destructuring pattern, and the value is Copy, Rust will silently copy every value I am iterating over?
Yes, that's correct, although the compiler backend might optimize the copying away.
If so, why does that happen?
Writing & before a pattern dereferences it. This copies the value it refers to, because a reference only borrows its value, so it can't move the value.
Example:
#[derive(Copy, Clone)]
struct Foo(i32);
let foo: Foo = Foo(5); // value is owned by foo
let borrow: &Foo = &foo; // value is borrowed
let bar: Foo = *borrow; // value is copied, the copy is owned by bar
// the original value is still owned by foo
Your first example is a bit special:
let _ = a.iter().filter(|&x| *x != Foo(1));
This seems at first as if it should require the Copy trait because x is dereferenced twice, first in the pattern, and then in the comparison.
However, the comparison is done by the PartialEq trait, which takes its arguments by reference. So the above is desugared to:
let _ = a.iter().filter(|&x| PartialEq::eq(&*x, &Foo(1)));
Which works because &* cancel each other out. Hence, x is only dereferenced once (in the pattern).

Related

Why is the move keyword needed when returning a closure which captures a Copy type?

godbolt
fn foo(c: char) -> impl Fn() -> i32 {
|| bar(c)
}
fn bar(_: char) -> i32 {
42
}
Which throws an error
error[E0597]: `c` does not live long enough
--> src/lib.rs:2:12
|
2 | || bar(c)
| -- ^ borrowed value does not live long enough
| |
| value captured here
3 | }
| -
| |
| `c` dropped here while still borrowed
| borrow later used here
I thought primitive types like char were copied by default; why do I need to explicitly move it (move || bar(c)) to get the code to compile?
You are correct that the char is Copy, but what's tricky is exactly when the copy is performed.
Pretend that the Copy trait had a method called copy, similar to Clone::clone. As an example of what the compiler does, your closure becomes:
|| {
let c = Copy::copy(&c);
bar(c)
}
The closure only captures c by reference as that's all it needs to perform the copy when the closure is invoked. By returning the closure, you'd be trying to return the reference to the local, which is forbidden.
Using move forces the closure to capture c by value.
See also:
How to copy instead of borrow an i64 into a closure in Rust?
Is there any way to return a reference to a variable created in a function?
Do all primitive types implement the Copy trait?
I thought primitive types like char were copied by default; why do I need to explicitly move it (move || bar(c)) to get the code to compile?
It's actually because char is Copy that the issue occurs: by default, Rust closures are "smart", so the way they capture things is as unrestrictive as possible.
This means if they can capture by reference they will capture by reference, if they can't but can capture by mut ref they'll do that, and only if they can't will they capture by value ("move").
Here, because char is Copy it can be captured by reference then copied into the callee. And so the closure does that, and you have to tell it to capture by value (using the move keyword) instead.

How do I extend a HashSet<String> with another HashSet<String>?

When I try to extend a HashSet<String> with another HashSet<String>:
use std::collections::HashSet;
let mut a = HashSet::new();
a.insert("foo".to_owned());
let mut b = HashSet::new();
b.insert("bar".to_owned());
let c = a.extend(&b);
I get:
error[E0271]: type mismatch resolving `<&HashSet<String> as IntoIterator>::Item == String`
--> src/main.rs:7:11
|
7 | let c = a.extend(&b);
| ^^^^^^ expected reference, found struct `String`
|
= note: expected reference `&String`
found struct `String`
How do I do this?
HashSet::extend() comes from the Extend trait and therefore accepts any iterator (or iterable), not just a set.
If you pass it the reference to a set, that reference is iterable, but the iterator produces references to the elements of the set - in this case &String. Since HashSet::<String>::extend() expects an iterator that produces actual strings, that doesn't compile. There are two ways to fix the issue:
by passing the set b to extend by value: a.extend(b);
by creating an explicit iterator out of b using b.iter() and cloning the values it produces: a.extend(b.iter().cloned()).
In the first variant, b will be consumed and therefore no longer usable by the caller, but the strings from it will be reused in a. In the second variant, b will remain usable, but its strings will be copied for storage in a.
Note that in neither variant does it make sense to capture the return value of extend(), since it operates by side effect and doesn't return a useful value.

Immutable access in rust

I am new to rust from python and have used the functional style in python extensively.
What I am trying to do is to take in a string (slice) (or any iterable) and iterate with a reference to the current index and the next index. Here is my attempt:
fn main() {
// intentionally immutable, this should not change
let x = "this is a
multiline string
with more
then 3 lines.";
// initialize multiple (mutable) iterators over the slice
let mut lineiter = x.chars();
let mut afteriter = x.chars();
// to have some reason to do this
afteriter.skip(1);
// zip them together, comparing the current line with the next line
let mut zipped = lineiter.zip(afteriter);
for (char1, char2) in zipped {
println!("{:?} {:?}", char1, char2);
}
}
I think it should be possible to get different iterators that have different positions in the slice but are referring to the same parts of memory without having to copy the string, but the error I get is as follows:
error[E0382]: use of moved value: `afteriter`
--> /home/alex/Documents/projects/simple-game-solver/src/src.rs:15:35
|
10 | let afteriter = x.chars();
| --------- move occurs because `afteriter` has type `std::str::Chars<'_>`, which does not implement the `Copy` trait
11 | // to have some reason to do this
12 | afteriter.skip(1);
| --------- value moved here
...
15 | let mut zipped = lineiter.zip(afteriter);
| ^^^^^^^^^ value used here after move
I also get a warning telling me that zipped does not need to be mutable.
Is it possible to instantiate multiple iterators over a single variable and if so how can it be done?
Is it possible to instantiate multiple iterators over a single variable and if so how can it be done?
If you check the signature and documentation for Iterator::skip:
fn skip(self, n: usize) -> Skip<Self>
Creates an iterator that skips the first n elements.
After they have been consumed, the rest of the elements are yielded. Rather than overriding this method directly, instead override the nth method.
You can see that it takes self by value (consumes the input iterator) and returns a new iterator. This is not a method which consumes the first n elements of the iterator in-place, it's one which converts the existing iterator into one which skips the first n elements.
So instead of:
let mut afteriter = x.chars();
afteriter.skip(1);
you just write:
let mut afteriter = x.chars().skip(1);
I also get a warning telling me that zipped does not need to be mutable.
That's because Rust for loop uses the IntoIterator trait, which moves the iterable into the loop. It's not creating a mutable reference, it's just consuming whatever the RHS is.
Therefore it doesn't care what the mutability of the variable. You do need mut if you iterate explicitly, or if you call some other "terminal" method (e.g. nth or try_fold or all), or if you want to iterate on the mutable reference (that's mostly useful for collections though), but not to hand off iterators to some other combinator method, or to a for loop.
A for loop takes self, if you will. Just as for_each does in fact.
Thanks to #Stargateur for giving me the solution. The .skip(1) takes ownership of afteriter and returns ownership to a version without the first element. What was happening before was ownership was lost on the .skip and so the variable could not be mutated anymore (I am pretty sure)

Reference before assignment in Rust

I am playing around with Rust references:
fn main() {
let str = String::from("Hallo");
let &x = &str;
}
This produces the following error:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:3:9
|
3 | let &x = &str;
| ^-
| ||
| |hint: to prevent move, use `ref x` or `ref mut x`
| cannot move out of borrowed content
What is going on here?
Adding to wiomoc's answer: depending on what language(s) you've previously known, variable declaration in Rust might be a little different. Whereas in C/C++ you explicitly have to declare that you want a pointer/reference variable:
int *p = &other_int;
In Rust it's enough to just use let, so the above would in Rust be
let p = &other_int;
and when you write
let &s = &string;
It pattern-matches that, so the Rust compiler reads it roughly as "I know I have a reference, and I want to bind whatever it is referring to to the name p". If you're not familiar with pattern-matching, a more obvious example (that works in Rust as well) would be
let point = (23, 42);
let (x, y) = point;
The second line unpacks the right-hand side to match the left-hand side (both are tuples of two values) and binds the variable names on the left to the values at the same position in the structure on the right. In the case above, it's less obvious that it's matching your "structural description".
The result of let &x = &str;, i.e. "I know &str is a reference, please bind whatever it refers to to the variable x" means that you're trying to have x be the same as str, when at that line all you have is a borrowed reference to str. That's why the compiler tells you you can't create an owned value (which x would be, because it's not being created as a reference) from a reference.
You dont need that &at let x
let str = String::from("Hallo");
let x = &str;
Or if you want to declare the type manually
let string = String::from("Hallo");
let x: &str = &string;

How to retrieve a user-defined type from a for loop?

I defined an Attribute type and I have a Vec<Attribute> that I am looping over to retrieve the "best" one. This was similar to my first attempt:
#[derive(Debug)]
struct Attribute;
impl Attribute {
fn new() -> Self {
Self
}
}
fn example(attrs: Vec<Attribute>, root: &mut Attribute) {
let mut best_attr = &Attribute::new();
for a in attrs.iter() {
if is_best(a) {
best_attr = a;
}
}
*root = *best_attr;
}
// simplified for example
fn is_best(_: &Attribute) -> bool {
true
}
I had the following compile error:
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:17:13
|
17 | *root = *best_attr;
| ^^^^^^^^^^ cannot move out of borrowed content
After some searching for a solution, I resolved the error by doing the following:
Adding a #[derive(Clone)] attribute to my Attribute struct
Replacing the final statement with *root = best_attr.clone();
I don't fully understand why this works, and I feel like this is a rough solution to the problem I was having. How does this resolve the error, and is this the correct way to solve this problem?
You are experiencing the basis of the Rust memory model:
every object can (and must!) be owned by only exactly one other object
most types are never implicitly copied and always moved (there are some exceptions: types that implement Copy)
Take this code for example:
let x = String::new();
let y = x;
println!("{}", x);
it generates the error:
error[E0382]: borrow of moved value: `x`
--> src/main.rs:4:20
|
3 | let y = x;
| - value moved here
4 | println!("{}", x);
| ^ value borrowed here after move
|
= note: move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait
x, of type String, is not implicitly copyable, and thus has been moved into y. x cannot be used any longer.
In your code, when you write *root = *best_attr, you are first dereferencing the reference best_attr, then assigning the dereferenced value to *root. Your Attribute type is not Copy, thus this assignment should be a move.
Then, the compiler complains:
cannot move out of borrowed content
Indeed, best_attr is an immutable reference, which does not allow you to take ownership of the value behind it (it doesn't even allow modifying it). Allowing such a move would put the object owning the value behind the reference in an undefined state, which is exactly what Rust aims to prevent.
In this case, your best option is indeed to create a new object with the same value as the first one, which is exactly what the trait Clone is made for.
#[derive(Clone)] allows you to mark your structs as Clone-able, as long as all of their fields are Clone. In more complex cases, you'll have to implement the trait by hand.

Resources