Rust如何向下转型非引用可变结构体解决可变方法调用报错
问题核心原因
报错的根本原因有两点:
- 你定义的
get_key_async方法要求&mut self可变引用参数,但downcast_ref返回的是不可变的&GoogleKeyProvider,无法直接调用可变方法 - 你用
Arc包裹GoogleKeyProvider用于多线程共享,Arc默认只能提供不可变引用,无法直接获取可变权限修改内部缓存字段
最优解决方案
推荐用内部可变性处理并发场景下的内部字段修改,同时取消不必要的向下转型操作,步骤如下:
- 调整
GoogleKeyProvider结构,加入线程安全的内部可变性容器
异步场景下推荐用tokio::sync::Mutex,如果缓存操作没有跨await的耗时逻辑,也可以用std::sync::Mutex:
use tokio::sync::Mutex; use std::time::Instant; #[derive(Clone)] pub struct GoogleKeyProvider { inner: Mutex<ProviderInner>, } // 把原来的字段放到内部结构体里,通过锁控制访问 struct ProviderInner { cached: Option<JwkSet>, expiration_time: Instant, } impl Default for GoogleKeyProvider { fn default() -> Self { Self { inner: Mutex::new(ProviderInner { cached: None, expiration_time: Instant::now(), }) } } }
- 修改Trait定义和实现,把可变参数改为不可变参数
因为内部字段修改已经通过锁控制,不需要外部传入可变引用:
#[async_trait] pub trait AsyncKeyProvider { // 把&mut self改为&self async fn get_key_async(&self, key_id: &str) -> Result<Option<Jwk>, ()>; fn as_any(&self) -> &dyn Any; } #[async_trait] impl AsyncKeyProvider for GoogleKeyProvider { async fn get_key_async(&self, key_id: &str) -> Result<Option<Jwk>, ()> { // 先拿到锁,再操作内部字段 let mut inner = self.inner.lock().await; // 原有逻辑不变,操作inner.cached、inner.expiration_time即可 // ... } fn as_any(&self) -> &dyn Any { self } }
- 修正actix-web的注册和handler逻辑,直接用Trait对象避免转型
你之前注册的Data类型和handler接收的类型不匹配,调整后可以直接用Trait调用方法,不需要向下转型:
// 注册时转成Trait对象 let key_provider: Arc<dyn AsyncKeyProvider> = Arc::new(GoogleKeyProvider::default()); let key_provider_data = web::Data::from(key_provider); let server = HttpServer::new(move || { App::new() .app_data(key_provider_data.clone()) .route("/validate", web::post().to(validate)) });
handler和validate_jwt直接用Trait参数即可:
pub async fn validate( jwt_body: web::Json<JwtBody>, provider: web::Data<Arc<dyn AsyncKeyProvider>> ) -> impl Responder { match validate_jwt(&jwt_body.jwt, provider.as_ref()).await { // ... } } async fn validate_jwt( jwt: &str, provider: &dyn AsyncKeyProvider ) -> Result<bool, Box<dyn std::error::Error>> { let key_to_use = provider.get_key_async("your_key_id").await.unwrap(); // ... }
必须向下转型的场景处理
如果你的业务逻辑确实需要拿到GoogleKeyProvider的具体类型实例,可以直接对Arc进行向下转型:
pub async fn validate( jwt_body: web::Json<JwtBody>, provider: web::Data<Arc<dyn AsyncKeyProvider>> ) -> impl Responder { // 把Arc<Trait>转成Arc<具体类型> let google_provider = provider .into_inner() .downcast::<GoogleKeyProvider>() .expect("Wasn't a GoogleKeyProvider"); match validate_jwt(&jwt_body.jwt, google_provider.as_ref()).await { // ... } }
内容的提问来源于stack exchange,提问作者dantdj
相关产品推荐
相关产品推荐

