Arc包裹的结构体方法中使用字段时出现move错误的解决办法
Arc包裹的结构体方法中使用字段时出现move错误的解决办法
嗨,我来帮你搞定这个问题~你遇到的错误核心原因很简单:在tokio::spawn的异步块里,你把self(也就是Arc<Task>实例)的所有权完全转移过去了,导致后面rx.await之后再想访问self的字段时,self已经被移走了,自然就触发了E0382错误。
解决思路
Arc的设计就是用来共享所有权的,它的clone()方法是轻量的——只会增加引用计数,不会复制底层的结构体数据。我们只需要在spawn任务前先克隆一份Arc<Task>的副本,把副本移到异步块里使用,保留原self供后续代码访问字段即可。
修正后的关键代码
重点看do_stuff方法的修改:
pub async fn do_stuff(self: Arc<Task>) -> Result<(), Error> { // 克隆Arc副本,给spawn的任务使用 let self_clone = self.clone(); let (tx, rx): (Sender<String>, Receiver<String>) = oneshot::channel(); tokio::spawn(async move { // 这里用克隆后的副本,原self不会被移走 self_clone.do_more_stuff(tx).await.unwrap_or_else(|err| { println!("ERROR: {err}"); }); }); match rx.await { Ok(data) => { // 原self依然有效,正常访问字段(这里甚至不需要clone字段,因为只是打印) println!("{}, {} took: {}", self.some_data, self.some_other_data, data) } Err(_) => { panic!(); } } Ok(()) }
完整修正代码
use anyhow::{Error, Result}; use std::sync::Arc; use tokio::time::{sleep, Duration}; use tokio::sync::oneshot::{self, Sender, Receiver}; use rand::Rng; #[derive(Debug)] pub struct Task { some_data: String, some_other_data: String, } impl Task { pub fn new(some_data: String, some_other_data: String) -> Self { Self {some_data, some_other_data} } pub async fn do_stuff(self: Arc<Task>) -> Result<(), Error> { let self_clone = self.clone(); let (tx, rx): (Sender<String>, Receiver<String>) = oneshot::channel(); tokio::spawn(async move { self_clone.do_more_stuff(tx).await.unwrap_or_else(|err| { println!("ERROR: {err}"); }); }); match rx.await { Ok(data) => { println!("{}, {} took: {}", self.some_data, self.some_other_data, data) } Err(_) => { panic!(); } } Ok(()) } pub async fn do_more_stuff(self: Arc<Task>, tx: Sender<String>) -> Result<(), Error> { let sleep_time = rand::thread_rng().gen_range(0..3); sleep(Duration::from_secs(sleep_time)).await; if let Err(_) = tx.send(sleep_time.to_string()) { panic!(); } Ok(()) } } #[tokio::main] async fn main() -> Result<(), Error> { let data_one = String::from("hello"); let data_two = String::from("world"); let mut handles = Vec::new(); for _ in 0..5 { let t = Arc::new(Task::new(data_one.clone(), data_two.clone())); handles.push( tokio::spawn(async move { t.do_stuff().await }) ); } futures::future::join_all(handles).await; Ok(()) }
额外说明
Arc::clone()是非常高效的操作,只是原子性地增加引用计数,完全不用担心性能问题。- 你原来代码里打印字段时用了
clone(),其实这里不需要——因为我们只是读取字段的内容打印,String实现了Display,直接引用即可(self.some_data会自动解引用为&String,而println!可以接受引用)。
备注:内容来源于stack exchange,提问作者Coldchain9
相关产品推荐
相关产品推荐

