У меня есть трейт репозитория с методами get
и set
, и я пытаюсь создать реализацию этого трейта, которая охватывает другую реализацию этого трейта. Я использую async
, поэтому все должно быть Send
, но мне все равно на Sync
. Хорошо, если этот код работает только в одном потоке.
Однако в моей оболочке я продолжаю получать ошибки, связанные с тем, что HashMap
используется в оболочке get
, не являющейся Sync
. Я не уверен, почему это проблема. Я думаю, технически get
можно было бы вызывать параллельно, но этот доступ также был бы неизменяемым, так что это не имело бы значения? И кроме того, я хочу, чтобы любой параллелизм обрабатывался на более высоком уровне, поэтому ошибка должна происходить не здесь, а в том месте, где эта функция будет вызываться параллельно.
Я смог заставить его работать, вставив HashMap
в Mutex
, но этого я очень хотел бы избежать.
use std::{marker::PhantomData, collections::HashMap};
use async_trait::async_trait;
#[derive(PartialEq, Eq, Hash)]
struct IVec3 {
pub x: i32,
pub y: i32,
pub z: i32
}
trait VoxelComponent : Copy + Send {}
enum VoxelRepositoryError{}
#[async_trait]
trait VoxelRepository<T: VoxelComponent> : Send {
async fn get(&self, position: IVec3) -> Result<Option<T>, VoxelRepositoryError>;
async fn set(&mut self, position: IVec3, component: Option<T>) -> Result<(), VoxelRepositoryError>;
}
struct DiffVoxelRepository<R: VoxelRepository<C>, C: VoxelComponent> {
source_repository: R,
cache: HashMap<IVec3, C>,
_component: PhantomData<fn() -> C>
}
impl<R: VoxelRepository<C>, C: VoxelComponent> DiffVoxelRepository<R, C> {
fn new(repository: R) -> DiffVoxelRepository<R, C> {
DiffVoxelRepository {
source_repository: repository,
cache: HashMap::new(),
_component: PhantomData
}
}
}
#[async_trait]
impl<R: VoxelRepository<C>, C: VoxelComponent> VoxelRepository<C> for DiffVoxelRepository<R, C> {
async fn get(&self, position: IVec3) -> Result<Option<C>, VoxelRepositoryError> {
match self.cache.get(&position) {
Some(value) => Ok(Some(*value)),
None => self.source_repository.get(position).await
}
}
async fn set(&mut self, position: IVec3, component: Option<C>) -> Result<(), VoxelRepositoryError>{
match component {
Some(c) => self.cache.insert(position, c),
None => self.cache.remove(&position)
};
self.source_repository.set(position, component).await
}
}
error: future cannot be sent between threads safely
--> src/lib.rs:39:85
|
39 | async fn get(&self, position: IVec3) -> Result<Option<C>, VoxelRepositoryError> {
| _____________________________________________________________________________________^
40 | | match self.cache.get(&position) {
41 | | Some(value) => Ok(Some(*value)),
42 | | None => self.source_repository.get(position).await
43 | | }
44 | | }
| |_____^ future created by async block is not `Send`
|
note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
--> src/lib.rs:39:19
|
39 | async fn get(&self, position: IVec3) -> Result<Option<C>, VoxelRepositoryError> {
| ^^^^ has type `&DiffVoxelRepository<R, C>` which is not `Send`, because `DiffVoxelRepository<R, C>` is not `Sync`
= note: required for the cast from `[async block@src/lib.rs:39:85: 44:6]` to the object type `dyn Future<Output = Result<Option<C>, VoxelRepositoryError>> + Send`
help: consider further restricting this bound
|
37 | impl<R: VoxelRepository<C> + std::marker::Sync, C: VoxelComponent> VoxelRepository<C> for DiffVoxelRepository<R, C> {
| +++++++++++++++++++
error[E0277]: `C` cannot be shared between threads safely
--> src/lib.rs:39:85
|
39 | async fn get(&self, position: IVec3) -> Result<Option<C>, VoxelRepositoryError> {
| _____________________________________________________________________________________^
40 | | match self.cache.get(&position) {
41 | | Some(value) => Ok(Some(*value)),
42 | | None => self.source_repository.get(position).await
43 | | }
44 | | }
| |_____^ `C` cannot be shared between threads safely
|
= note: required because it appears within the type `(IVec3, C)`
= note: required for `hashbrown::raw::RawTable<(IVec3, C)>` to implement `Sync`
= note: required because it appears within the type `HashMap<IVec3, C, RandomState>`
= note: required because it appears within the type `HashMap<IVec3, C>`
note: required because it appears within the type `DiffVoxelRepository<R, C>`
--> src/lib.rs:20:8
|
20 | struct DiffVoxelRepository<R: VoxelRepository<C>, C: VoxelComponent> {
| ^^^^^^^^^^^^^^^^^^^
= note: required for `&DiffVoxelRepository<R, C>` to implement `Send`
note: required because it's used within this `async` block
--> src/lib.rs:39:85
|
39 | async fn get(&self, position: IVec3) -> Result<Option<C>, VoxelRepositoryError> {
| _____________________________________________________________________________________^
40 | | match self.cache.get(&position) {
41 | | Some(value) => Ok(Some(*value)),
42 | | None => self.source_repository.get(position).await
43 | | }
44 | | }
| |_____^
= note: required for the cast from `[async block@src/lib.rs:39:85: 44:6]` to the object type `dyn Future<Output = Result<Option<C>, VoxelRepositoryError>> + Send`
help: consider further restricting this bound
|
37 | impl<R: VoxelRepository<C>, C: VoxelComponent + std::marker::Sync> VoxelRepository<C> for DiffVoxelRepository<R, C> {
|
Я использую
async
, поэтому все должно бытьSend
, но мне все равно наSync
. Хорошо, если этот код работает только в одном потоке.
Использование async
не требует от вас использования Send
— этого требуют только многопоточные асинхронные исполнители. Однако библиотека async_trait
по умолчанию предполагает, что вы хотите Send
для всех фьючерсов. Вы можете попросить его не с #[async_trait(?Send)]
.
Следующая версия вашей программы компилируется со следующими изменениями:
Send
.#[async_trait]
на #[async_trait(?Send)]
.derive(Clone, Copy)
в IVec3
. (Это несвязанная проблема, обнаруженная после того, как другие были исправлены.)Если вместо этого вы хотите сохранить Send
границы, то вам действительно нужно будет добавить Sync
границы к типам, с которыми вы работаете, через &
ссылку из Send
фьючерсов, потому что, как правило, &T: Send
только когда T: Sync
, поэтому с помощью &T
ссылки из Send
будущего требует T
реализации Sync
. Send
и Sync
тесно связаны, и обычно вы не можете использовать Send
, не используя иногда Sync
.
use std::{marker::PhantomData, collections::HashMap};
use async_trait::async_trait;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
struct IVec3 {
pub x: i32,
pub y: i32,
pub z: i32
}
trait VoxelComponent : Copy {}
enum VoxelRepositoryError{}
#[async_trait(?Send)]
trait VoxelRepository<T: VoxelComponent> {
async fn get(&self, position: IVec3) -> Result<Option<T>, VoxelRepositoryError>;
async fn set(&mut self, position: IVec3, component: Option<T>) -> Result<(), VoxelRepositoryError>;
}
struct DiffVoxelRepository<R: VoxelRepository<C>, C: VoxelComponent> {
source_repository: R,
cache: HashMap<IVec3, C>,
_component: PhantomData<fn() -> C>
}
impl<R: VoxelRepository<C>, C: VoxelComponent> DiffVoxelRepository<R, C> {
fn new(repository: R) -> DiffVoxelRepository<R, C> {
DiffVoxelRepository {
source_repository: repository,
cache: HashMap::new(),
_component: PhantomData
}
}
}
#[async_trait(?Send)]
impl<R: VoxelRepository<C>, C: VoxelComponent> VoxelRepository<C> for DiffVoxelRepository<R, C> {
async fn get(&self, position: IVec3) -> Result<Option<C>, VoxelRepositoryError> {
match self.cache.get(&position) {
Some(value) => Ok(Some(*value)),
None => self.source_repository.get(position).await
}
}
async fn set(&mut self, position: IVec3, component: Option<C>) -> Result<(), VoxelRepositoryError>{
match component {
Some(c) => self.cache.insert(position, c),
None => self.cache.remove(&position)
};
self.source_repository.set(position, component).await
}
}
Почему ты пишешь #[async_trait(?Send)]
? с дополнительным аргументом? Согласно его источникам, он не использует этот docs.rs/async-trait/latest/src/async_trait/lib.rs.html#341
@unegare Я следил за документацией и подтвердил, что она скомпилирована. Но также в коде вы можете видеть, что args
передается в impl Parse for Args, который затем ищет ?Send
или ничего.
Но возможность поделиться
&T
между потоками — это именно то, что означаетT: Sync
. Представьте, что уT
есть какая-то внутренняя изменчивость (RefCell
илиRc
), тогда вы разделяете&T
между потоками и... состоянием гонки! Если вы знаете, что этого просто не произойдет, вы должны сообщить об этом компилятору с помощьюT: Sync
.