I'd like to take SomeType
out of Result<Vec<Data<&SomeType>>>
, and then pass it by a channel, but I failed:
pub fn read(result: Result<Vec<Data<&SomeType>>, ()>, tx: mpsc::Sender<SomeType>) {
if let Ok(datas) = result {
for data in datas.iter() {
let actual_data = data.value();
let some_type_instance = SomeType { k: 1 };
tx.send(some_type_instance); // works
tx.send(**actual_data); // errors
}
}
}
errors with:
error[E0507]: cannot move out of `**actual_data` which is behind a shared reference
--> src/main.rs:40:21
|
40 | tx.send(**actual_data);
| ^^^^^^^^^^^^^ move occurs because `**actual_data` has type `SomeType`, which does not implement the `Copy` trait
It seems that tx
didn't take the ownership correctly. Although implementing the Copy
trait on SomeType
can eliminate the error, I am not sure if Copy
or Clone
would reduce the performance. I am struggling with it but couldn't find a correct way to fix it.
The following is a complete code to regenerate the error.
use std::result::Result;
use std::sync::mpsc;
pub struct SomeType {
k: i32,
}
pub struct Data<D> {
value: D,
}
impl<D> Data<D> {
pub fn value(&self) -> &D {
&self.value
}
}
pub fn main() {
let a = SomeType { k: 1 };
let b = SomeType { k: 2 };
let c = SomeType { k: 3 };
let A = Data { value: &a };
let B = Data { value: &b };
let C = Data { value: &c };
let datas = vec![A, B, C];
let result = Ok(datas);
let (tx, rx) = mpsc::channel();
read(result, tx);
}
pub fn read(result: Result<Vec<Data<&SomeType>>, ()>, tx: mpsc::Sender<SomeType>) {
if let Ok(datas) = result {
for data in datas.iter() {
let actual_data = data.value();
let some_type_instance = SomeType { k: 1 };
tx.send(some_type_instance); // this line works
tx.send(**actual_data); // this line errors
}
}
}
When all you have is a &T
, you cannot get a T
without cloning the value behind the reference, because extracting a non-copy value would move it, and the owner of the T
(here Data
) who gave out the reference expects the value to remain valid.
However, if you control the type stored into Data
, you can wrap your actual value in an Option
. Then you can use std::mem::replace(ref_to_t, None)
to obtain the value behind the reference and leave None
in its place. Option
even has a take()
method that does that for you.
But both mem::replace()
and Option::take()
require a mutable reference, and all you have is a shared reference. To work around that, you need to also use interior mutability, such as provided by RefCell
. The type you will then put in Data
is RefCell<Option<SomeType>>
- don't be put off by the nested generics, just read them as "RefCell
containing an optional SomeType
"). RefCell
has a borrow_mut()
method giving you a mutable reference to the inner content, on which you can then call Option::take()
, or you can call RefCell::take()
which will do the right thing by itself.
pub fn main() {
let a = SomeType { k: 1 };
let b = SomeType { k: 2 };
let c = SomeType { k: 3 };
let da = Data {
value: RefCell::new(Some(a)),
};
let db = Data {
value: RefCell::new(Some(b)),
};
let dc = Data {
value: RefCell::new(Some(c)),
};
let datas = vec![da, db, dc];
let (tx, _rx) = mpsc::channel();
read(&datas, tx);
}
pub fn read(datas: &[Data<RefCell<Option<SomeType>>>], tx: mpsc::Sender<SomeType>) {
for data in datas {
let actual_data = data.value().take().unwrap();
tx.send(actual_data).unwrap();
}
}