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 aTwithout cloning the value behind the reference, because extracting a non-copy value would move it, and the owner of theT(hereData) 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 anOption. Then you can usestd::mem::replace(ref_to_t, None)to obtain the value behind the reference and leaveNonein its place.Optioneven has atake()method that does that for you.But both
mem::replace()andOption::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 byRefCell. The type you will then put inDataisRefCell<Option<SomeType>>- don't be put off by the nested generics, just read them as "RefCellcontaining an optionalSomeType").RefCellhas aborrow_mut()method giving you a mutable reference to the inner content, on which you can then callOption::take(), or you can callRefCell::take()which will do the right thing by itself.