【问题标题】:How to specify a Rust struct field must satisfy a trait when the struct has async methods?当结构具有异步方法时,如何指定 Rust 结构字段必须满足特征?
【发布时间】:2021-05-08 08:12:28
【问题描述】:

创建身份验证服务器。

我想要一个PGUserRepo 结构,它实现了UserRepo trait async fn create

PGUserRepo 应该有一个实现 Hasher 特征的字段。

但是,当我尝试将 PGUserRepo 字段限制为 dyn Hasher 时,出现错误

“特性std::marker::Send 没有为`(dyn domain::hasher::Hasher + 'static)”实现。

如果我将PGUserRepo 字段设为具体实现类型ArgonHasher,则不会出错。但我不希望PGUserRepo 关心给了它什么样的Hasher,只是它实现了Hasher 特征。

我尝试将 hasher 字段包装在 Box、Arc 和 Mutex 的各种组合中,但我不明白为什么称它为 ArgonHasher 很好但说它是 Hasher 的一些实现者的根本原因不是。

代码,为方便起见,折叠成一个文件:


pub trait Hasher {
    fn hash(&self, password: String) -> Result<String, HasherError>;
}

pub struct ArgonHasher {}

impl Hasher for ArgonHasher {
    fn hash(&self, password: String) -> Result<String, HasherError> {
        let result = argon2id13::pwhash(
            &password.as_bytes(),
            argon2id13::OPSLIMIT_INTERACTIVE,
            argon2id13::MEMLIMIT_INTERACTIVE,
        );
        match result {
            Ok(hashed_password_result) => match std::str::from_utf8(&hashed_password_result.0) {
                Ok(hashed_password_utf8) => Ok(String::from(hashed_password_utf8)),
                Err(e) => Err(HasherError::from(e)),
            },
            Err(e) => Err(HasherError::from(e)),
        }
    }
}

impl From<()> for HasherError {
    fn from(_: ()) -> Self {
        HasherError::HasherError {
            message: String::from(""),
        }
    }
}

impl From<std::str::Utf8Error> for HasherError {
    fn from(cause: std::str::Utf8Error) -> Self {
        HasherError::HasherError {
            message: format!("{}", cause),
        }
    }
}

#[async_trait]
pub trait UserRepo {
    async fn create(&self, user: User) -> Result<User, UserRepoError>;
}

#[derive(Debug)]
pub enum UserRepoError {
    UserAlreadyExistsError { field: String, value: String },
    UserRepoError { message: String },
}

impl fmt::Display for UserRepoError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *&self {
            UserRepoError::UserAlreadyExistsError { field, value } => {
                f.write_str(&format!("User with {}={} already exists", field, value))
            }
            UserRepoError::UserRepoError { message } => f.write_str(&message),
        }
    }
}


pub struct PGUserRepo {
    conn_pool: PgPool,
    hasher: &'static dyn Hasher,
}

impl PGUserRepo {
    pub fn new(conn_pool: PgPool, hasher: &'static dyn Hasher) -> Self {
        PGUserRepo { conn_pool, hasher }
    }
}

#[async_trait]
impl UserRepo for PGUserRepo {
    async fn create(&self, user: User) -> Result<User, UserRepoError> {
    # Compiler error is on this function
    # hasher is not even used in this function yet, it's just a field on PGuserRepo
}

奖励:我真的不需要 Hasher 特征 hash 来引用 self 但没有它我会得到“错误[E0038]:特征 domain::hasher::Hasher 不能被制成对象” .

【问题讨论】:

标签: rust async-await


【解决方案1】:

标记特征Send 用于指示何时可以安全地在线程之间传输类型。当编译器认为它是安全的时,它默认实现。但是,您有一个 trait 对象 hasher,它对是否可以在线程之间安全共享没有任何限制。

之所以出现这里是因为async 代码通常通过多个线程处理,而async_trait 强制执行此操作。

修复是指出只允许线程间共享的Hashers。你可以通过使用Sync trait 来做到这一点:

pub struct PGUserRepo {
    conn_pool: PgPool,
    hasher: &'static (dyn Hasher + Sync),
}

请参阅Understanding the Send trait,详细了解SyncSend 之间的区别及其用途。

【讨论】:

  • (dyn Hasher + Sync + Send) 允许使用它。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多