Hm, I thought I understood it all, but then I bumped into this:
fn just_looking(s: &Option<String>) {
println!("Is this Some? {}", s.is_some())
}
fn main() {
let s = String::from("yo");
just_looking(&Some(s));
println!("{s}");
}
This does not compile because I supposedly move s:
Compiling playground v0.0.1 (/playground)
error[E0382]: borrow of moved value: `s`
--> src/main.rs:8:15
|
6 | let s = String::from("yo");
| - move occurs because `s` has type `String`, which does not implement the `Copy` trait
7 | just_looking(&Some(s));
| - value moved here
8 | println!("{s}");
| ^^^ value borrowed here after move
|
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider cloning the value if the performance cost is acceptable
|
7 | just_looking(&Some(s.clone()));
| ++++++++
For more information about this error, try `rustc --explain E0382`.
error: could not compile `playground` (bin "playground") due to 1 previous error
I don't understand - how is s being moved when just_looking is only taking a reference? Where is it moved to? Can't I access an &Option<String> without moving it? Thanks!
You need to use Option<&T> in function arguments instead of &Option<T>.
Call .as_ref() or .as_deref() on Options owning their data. It will transform &Option<String> to Option<&String> or Option<&str>, which allow you to destroy the Option and/or copy the reference from inside it.
When you write Some(s), you move s into a Some. It doesn’t matter that you then take a reference — in order to take a reference to an Option<String>, an Option<String> has to exist in memory, which requires the Option to own the String (or be None).
Generally, when in this type of situation, you should work with Option<&str> instead.
fn just_looking(s: Option<&str>) {
println!("Is this Some? {}", s.is_some())
}
fn main() {
let s = String::from("yo");
just_looking(Some(&s));
println!("{s}");
}
Then, when you do have an &Option<String>, you can use Option::as_ref() (for simple owned values) or Option::as_deref() (for String-to-str) to conveniently convert from that form to the Option<&T> form.
Thanks, but the example I gave was simplified from my real problem. A bit more similar to my problem would be:
struct A {}
struct B {}
struct C {}
enum ABC {
A(A),
B(B),
C(C)
}
fn look_at_abc(abc: &ABC) { todo!() }
fn consume_a(a: A) { todo!() }
fn main() {
let a = A {};
look_at_abc(&ABC::A(a));
consume_a(a);
}
which fails with
error[E0382]: use of moved value: `a`
--> src/main.rs:18:15
|
16 | let a = A {};
| - move occurs because `a` has type `A`, which does not implement the `Copy` trait
17 | look_at_abc(&ABC::A(a));
| - value moved here
18 | consume_a(a);
| ^ value used here after move
|
note: if `A` implemented `Clone`, you could clone the value
--> src/main.rs:1:1
|
1 | struct A {}
| ^^^^^^^^ consider implementing `Clone` for this type
...
17 | look_at_abc(&ABC::A(a));
| - you could clone this value
For more information about this error, try `rustc --explain E0382`.
I suppose I could create a temporary value and destructure it again, but it's really awkward. Any better options? Thanks!
Or you can make look_at_abc generic, if there is a reasonable way to define a trait that captures what you want out of ABC:
fn look_at_abc<T: Thing>(value: &T) {...}
trait Thing {...}
impl Thing for A {...}
impl Thing for B {...}
impl Thing for C {...}
impl Thing for ABC {...}
Thanks! In my case, the abc is mostly used as a unique key to look up things in a map. I suppose I could implement some kind of hashing (but at least 128 bits or so to avoid collisions), where the hash is Copy.
For now, I might just go with this:
struct A {}
struct B {}
struct C {}
enum ABC {
A(A),
B(B),
C(C)
}
fn look_at_abc(abc: &ABC) { println!("Looking at abc.") }
fn consume_a(a: A) { println!("Consuming a.") }
fn main() {
let a = A {};
let abc = ABC::A(a);
look_at_abc(&abc);
let ABC::A(a) = abc else { unreachable!() };
consume_a(a);
}