Как настроить отправку и синхронизацию для структуры с динамическим элементом (будущее не может быть безопасно отправлено между потоками)

#asynchronous #rust #concurrency

Вопрос:

Рассмотрим следующий код, который объявляет признак с async помощью метода, использующего async-trait ящик.

 use std::{io::Result, sync::Arc};

use async_trait::async_trait;
use tokio;

// A trait that describes a power source.
trait PowerSource {
    fn supply_power(amp;self) -> Result<()>;
}

// ElectricMotor implements PowerSource.
struct ElectricMotor {}

impl PowerSource for ElectricMotor {
    fn supply_power(amp;self) -> Result<()> {
        println!("ElectricMotor::supply_power");
        Ok(())
    }
}

// A trait that describes a vehicle
#[async_trait]
trait Vehicle {
    async fn drive(amp;self) -> Result<()>;
}

// An automobile has some kind of power source and implements Vehicle
struct Automobile {
    power_source: Arc<dyn PowerSource>,
}

#[async_trait]
impl Vehicle for Automobile {
    async fn drive(amp;self) -> Result<()> {
        self.power_source.supply_power()?;
        println!("Vehicle::Drive");
        Ok(())
    }
}

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let driver = ElectricMotor {};
    let controller = Automobile {
        power_source: Arc::new(driver),
    };

    controller.drive().await?;

    Ok(())
}
 

Это не компилируется с ошибкой «будущее не может быть безопасно отправлено между потоками»:

 error: future cannot be sent between threads safely
  --> src/main.rs:34:41
   |
34 |       async fn drive(amp;self) -> Result<()> {
   |  _________________________________________^
35 | |         self.power_source.supply_power()?;
36 | |         println!("Vehicle::Drive");
37 | |         Ok(())
38 | |     }
   | |_____^ future created by async block is not `Send`
   |
   = help: the trait `Sync` is not implemented for `(dyn PowerSource   'static)`
note: captured value is not `Send`
  --> src/main.rs:34:21
   |
34 |     async fn drive(amp;self) -> Result<()> {
   |                     ^^^^ has type `amp;Automobile` which is not `Send`
   = note: required for the cast to the object type `dyn Future<Output = Result<(), std::io::Error>>   Send`

error: future cannot be sent between threads safely
  --> src/main.rs:34:41
   |
34 |       async fn drive(amp;self) -> Result<()> {
   |  _________________________________________^
35 | |         self.power_source.supply_power()?;
36 | |         println!("Vehicle::Drive");
37 | |         Ok(())
38 | |     }
   | |_____^ future created by async block is not `Send`
   |
   = help: the trait `Send` is not implemented for `(dyn PowerSource   'static)`
note: captured value is not `Send`
  --> src/main.rs:34:21
   |
34 |     async fn drive(amp;self) -> Result<()> {
   |                     ^^^^ has type `amp;Automobile` which is not `Send`
   = note: required for the cast to the object type `dyn Future<Output = Result<(), std::io::Error>>   Send`
 

Если я правильно понимаю ошибку, он думает, что Automobile это не Send потому power_source , что свойства нет, и поэтому он не может создать правильное будущее. Я понимаю , что Arc это потокобезопасно и реализует Send и Sync , но я все еще очень новичок в параллелизме Rust и все еще не совсем понимаю, что это значит.

Как бы я исправил эту ошибку?

Ответ №1:

Вы должны изменить свое Automobile определение:

 struct Automobile {
    power_source: Arc<dyn PowerSource>,
}
 

В rust тип существует Send тогда и только тогда, когда все его члены Send (если вы не реализуете вручную и небезопасно Send ). То же самое относится и к Sync . Так что , учитывая, что вашего power_source нет Send , то сгенерированного impl Future тоже не будет Send .

Способ исправить это-добавить Send Sync требование для power_source :

 struct Automobile {
    power_source: Arc<dyn PowerSource   Send   Sync>,
}
 

Но почему Sync вы могли бы спросить ? Компилятор жалуется только на Send то, что в конце концов. Причина, по которой это требуется Sync , заключается в том, что Arc<T> реализует Send только если T и Send то, и другое Sync

Дополнительное чтение:

Комментарии:

1. Так просто, как только вы научитесь отделять деревья от леса. Спасибо.