Reputation: 8092
I want to perform an operation if and only if a
and b
are not None
. I do not want create a copy of complex structures, which is why struct X
does not implement Clone
.
use std::sync::{Arc, Mutex};
use std::cell::RefCell;
#[derive(Debug)]
struct X {
d: u32,
}
struct Foo {
a: Option<X>,
b: Option<u32>,
c: u32,
}
fn main() {
let smart_ptr = Arc::new(Mutex::new(RefCell::new(Foo {
a: Some(X { d: 1 }),
b: Some(2),
c: 3,
})));
{
let lock = smart_ptr.lock().unwrap();
let foo = lock.borrow();
if let (Some(ref a), Some(b)) = (foo.a, foo.b) {
println!("a: {:?}, b: {}", a, b);
}
}
}
If I try to compile this code, I get:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:25:42
|
25 | if let (Some(ref a), Some(b)) = (foo.a, foo.b) {
| ^^^ cannot move out of borrowed content
How should I fix the if
statement to get what I want without compilation errors?
Upvotes: 0
Views: 155
Reputation: 11573
You need to tell the if let
statement to only reference a
and b
to foo.a
and foo.b
, otherwise if let
would move some parts out of the borrowed value. This is forbidden for two reasons:
foo.a
into a
, "stealing away" the struct from your lender, then the value residing in foo.a
would no longer be valid, making foo
also invalid.One solution would be to duplicate the values Foo::a
and Foo::b
via clone()
which is - as you noted - unneeded and performance wise not optimal, especially since it would mean a deep copy.
I got it working without copying:
if let (&Some(ref a), &Some(b)) = (&foo.a, &foo.b) {
println!("a: {:?}, b: {}", a, b);
}
Upvotes: 4
Reputation: 5216
You can destructure Foo
instead of creating a tuple:
if let Foo { a: Some(ref a), b: Some(b), .. } = *foo {
println!("a: {:?}, b: {}", a, b);
}
Upvotes: 3
Reputation: 430851
Using a smaller example:
struct Foo {
a: Option<String>,
b: Option<String>,
}
fn main() {
let foo = &Foo {
a: Some("hi".into()),
b: Some("world".into()),
};
if let (Some(a), Some(b)) = (foo.a, foo.b) {
println!("a: {}, b: {}", a, b);
}
}
You can use Option::as_ref
to perform the same type of matching on a reference as the previous answer:
if let (Some(a), Some(b)) = (foo.a.as_ref(), foo.b.as_ref()) {
println!("a: {}, b: {}", a, b);
}
Upvotes: 6