在Map结构体字段中实现Trait泛型的Rust技术问题
基于Rocket的Security Voter权限控制实现方案
问题背景
想要在Rocket项目中实现类似Symfony Security Voter的权限控制体系,设计Security结构体存储不同主题(如"books""authors")对应的权限处理器(实现SecurityVoter trait的结构体),并将Security实例存入Rocket的State中。初始实现遇到编译错误,调整后仍不确定方案合理性,寻求正确实现建议。
初始代码
SecurityVoter trait定义
pub trait SecurityVoter { /// Gets the "subject" supported by the voter fn supports(&self) -> String; /// Takes a given right and a user, and checks if the user has_access to the action represented by the right. fn has_access(&self, right: &str, user: User) -> bool; }
Security结构体定义
#[derive(Default)] pub struct Security<T> where T: SecurityVoter, { voters: HashMap<String, T>, }
Security方法实现
impl<T> Security<T> where T: SecurityVoter, { pub fn add_handler(&mut self, handler: T) -> &mut Self { self.voters.insert(handler.supports(), handler); self } pub fn has_access(&self, subject: &str, right: &str, user: &User) -> bool { if self.voters.contains_key(subject) { return self.voters.get(subject).unwrap().has_access(right, user); } false } }
编译错误信息
error[E0599]: the function or associated item `default` exists for struct `core::security::Security<dyn core::security::SecurityVoter>`, but its trait bounds were not satisfied --> src/core/security.rs:68:59 | 6 | / pub struct Security<T> 7 | | where 8 | | T: SecurityVoter, 9 | | { 10 | | voters: HashMap<String, T>, 11 | | } | | - | | | | |_function or associated item `default` not found for this | doesn't satisfy `_: std::default::Default` ... 38 | pub trait SecurityVoter { | ----------------------- | | | doesn't satisfy `_: std::default::Default` | doesn't satisfy `_: std::marker::Sized` ... 68 | let mut security = Security::<dyn SecurityVoter>::default(); // <- line with the error | ^^^^^^^ function or associated item cannot be called on `core::security::Security<dyn core::security::SecurityVoter>` due to unsatisfied trait bounds | note: the following trait bounds were not satisfied: `dyn core::security::SecurityVoter: std::default::Default` `dyn core::security::SecurityVoter: std::marker::Sized` --> src/core/security.rs:5:10 | 5 | #[derive(Default)] | ^^^^^^^ unsatisfied trait bound introduced in this `derive` macro = note: the following trait bounds were not satisfied: `dyn core::security::SecurityVoter: std::marker::Sized` which is required by `core::security::Security<dyn core::security::SecurityVoter>: std::default::Default` `dyn core::security::SecurityVoter: std::default::Default` which is required by `core::security::Security<dyn core::security::SecurityVoter>: std::default::Default`
修改后的尝试代码
pub struct Security<T: ?Sized> { voters: HashMap<String, Box<T>>, } impl<T> Security<T> where T: SecurityVoter + ?Sized, { pub fn new() -> Self { Self { voters: HashMap::new(), } } pub fn add_handler(&mut self, handler: Box<T>) -> &mut Self { self.voters.insert(handler.supports(), handler); self } pub fn is_granted(user: &User, roles: Vec<&str>) -> bool { roles .iter() .all(|item| user.roles.contains(&item.to_string())) } pub fn has_access(&self, subject: &str, right: &str, user: &User) -> bool { if self.voters.contains_key(subject) { return self.voters.get(subject).unwrap().has_access(right, user); } false } } // ... #[cfg(test)] mod tests { use super::*; #[derive(Default)] struct TestSecurityHandler {} impl SecurityVoter for TestSecurityHandler { fn supports(&self) -> String { "test".to_string() } fn has_access(&self, right: &str, _user: &User) -> bool { if right == "ACCEPT_ACCESS" { return true; } false } } #[derive(Default)] struct STestSecurityHandler {} impl SecurityVoter for STestSecurityHandler { fn supports(&self) -> String { "test".to_string() } fn has_access(&self, right: &str, _user: &User) -> bool { if right == "ACCEPT_ACCESS" { return true; } false } } #[test] fn test_custom_security_handler() { let mut security = Security::<dyn SecurityVoter>::new(); // <- line with the error security.add_handler(Box::new(TestSecurityHandler::default())); security.add_handler(Box::new(STestSecurityHandler::default())); let user = User::default(); assert!(security.has_access("test", "ACCEPT_ACCESS", &user)); } }
正确实现建议
1. 简化Security结构体设计(移除泛型)
因为需要存储多种不同类型的SecurityVoter实现,直接使用trait对象+Box存储,无需给Security加泛型,避免Sized约束问题:
use std::collections::HashMap; pub struct Security { voters: HashMap<String, Box<dyn SecurityVoter>>, }
2. 修正SecurityVoter trait的约束与方法签名
- 为适配Rocket State的要求(
Send + Sync + 'static),给trait添加对应的约束 - 统一
has_access的user参数为引用类型,避免不必要的拷贝:
pub trait SecurityVoter: Send + Sync + 'static { fn supports(&self) -> String; fn has_access(&self, right: &str, user: &User) -> bool; }
3. 完善Security的方法实现
new方法直接创建空的HashMapadd_handler自动为实现类装箱,简化调用方代码- 优化
has_access的错误处理(避免unwrap):
impl Security { pub fn new() -> Self { Self { voters: HashMap::new(), } } // 自动接收任意实现SecurityVoter的类型并装箱 pub fn add_handler<V: SecurityVoter>(&mut self, handler: V) -> &mut Self { let subject = handler.supports(); self.voters.insert(subject, Box::new(handler)); self } pub fn is_granted(user: &User, roles: &[&str]) -> bool { roles .iter() .all(|&role| user.roles.contains(&role.to_string())) } pub fn has_access(&self, subject: &str, right: &str, user: &User) -> bool { self.voters .get(subject) .map_or(false, |voter| voter.has_access(right, user)) } }
4. 调整测试代码
无需手动指定泛型和装箱,直接调用方法:
#[cfg(test)] mod tests { use super::*; #[derive(Default)] struct TestSecurityHandler {} impl SecurityVoter for TestSecurityHandler { fn supports(&self) -> String { "test".to_string() } fn has_access(&self, right: &str, _user: &User) -> bool { right == "ACCEPT_ACCESS" } } #[derive(Default)] struct STestSecurityHandler {} impl SecurityVoter for STestSecurityHandler { fn supports(&self) -> String { "test".to_string() } fn has_access(&self, right: &str, _user: &User) -> bool { right == "ACCEPT_ACCESS" } } #[test] fn test_custom_security_handler() { let mut security = Security::new(); security.add_handler(TestSecurityHandler::default()); security.add_handler(STestSecurityHandler::default()); let user = User::default(); assert!(security.has_access("test", "ACCEPT_ACCESS", &user)); assert!(!security.has_access("test", "DENY_ACCESS", &user)); assert!(!security.has_access("unknown", "ACCEPT_ACCESS", &user)); } }
5. 集成到Rocket State
在Rocket启动时初始化Security实例并放入State:
use rocket::Build; #[launch] fn rocket() -> _ { let mut security = Security::new(); // 添加业务相关的权限处理器 security.add_handler(TestSecurityHandler::default()); rocket::build() .manage(security) // 将Security存入State .mount("/", routes![...]) }
在请求处理函数中使用State:
use rocket::State; #[get("/books/<id>")] fn get_book(id: u32, security: &State<Security>, user: &User) -> Result<String, String> { if security.has_access("books", "VIEW", user) { Ok(format!("Book {}", id)) } else { Err("Unauthorized".to_string()) } }
内容的提问来源于stack exchange,提问作者Adrien Gras
相关产品推荐
相关产品推荐

