如何在Rust中创建泛型容器工厂方法?解决编译大小未知错误
问题描述
我定义了如下容器类型:
struct Container<T: Sized + MyTrait + Serialize> { data: T, key: String }
它封装了这个 trait:
trait MyTrait { fn do_something(&self) -> Something; }
我有多个MyTrait的具体实现类型:
struct S1 {} impl MyTrait for S1 { ... }; // ... 多种类似类型 ... struct S10{} impl MyTrait for S10 { ... }
我需要一个上层工厂方法,根据决策逻辑返回合适的实现:
fn get_appropriate_impl(type_of_instance: SomeEnum ... ) -> Container<MyTrait> { // ...选择合适的实现返回... return match type_of_instance { // 每个分支返回不同类型 } }
但编译时出现错误:
Container<MyTrait> { doesn't have a size known at compile-time
我尝试了几种写法:
Container<Box<MyTrait>> Box<Container<MyTrait>> Box<Container<Box<MyTrait>>>
每种写法都有各自的错误。另外,我已经在Container上实现了Deref和DerefMut:
impl<T: MyTrait > Deref for Container<T> { type Target = T; fn deref(&self) -> &Self::Target { &self.data } }
请问该如何解决这个问题?
解决方案
核心问题是**MyTrait属于动态大小类型(DST)**,而你的Container明确要求泛型参数T必须是Sized(定义里的T: Sized + ...约束),直接用Container<MyTrait>必然报错,因为DST无法在编译期确定内存大小。
以下是可行的解决思路:
1. 推荐方案:让Container持有Box<dyn MyTrait>
保持原Container的Sized约束不变,将泛型参数T替换为Box<dyn MyTrait>——因为Box<dyn MyTrait>是固定大小的指针类型,完全满足Sized要求,同时能通过动态分发调用MyTrait的方法。
具体步骤
- 先确保
MyTrait满足对象安全,并且继承Serialize(或让Box<dyn MyTrait>满足Serialize约束):// 让MyTrait继承Serialize,确保Box<dyn MyTrait>满足Container的泛型约束 trait MyTrait: Serialize { fn do_something(&self) -> Something; } - 修改工厂方法的返回值和实现:
你之前实现的fn get_appropriate_impl(type_of_instance: SomeEnum) -> Container<Box<dyn MyTrait>> { match type_of_instance { SomeEnum::Type1 => Container { data: Box::new(S1 {}), key: "s1".to_string(), }, SomeEnum::Type10 => Container { data: Box::new(S10 {}), key: "s10".to_string(), }, // 其他分支实现... } }Deref依然有效:因为T是Box<dyn MyTrait>,解引用后会直接指向内部的MyTrait实例,调用do_something时自动触发动态分发。
2. 可选优化:放宽Container的Sized约束
如果不需要强制T必须是固定大小类型,可以把Container的泛型约束改为T: ?Sized + MyTrait + Serialize,这样它可以直接持有DST类型的引用或智能指针:
struct Container<T: ?Sized + MyTrait + Serialize> { data: T, key: String }
此时Container<dyn MyTrait>本身会变成DST,需要用Box包裹才能作为返回值:
fn get_appropriate_impl(type_of_instance: SomeEnum) -> Box<Container<dyn MyTrait>> { match type_of_instance { SomeEnum::Type1 => Box::new(Container { data: S1 {}, key: "s1".to_string(), }), // 其他分支... } }
这种写法更灵活,但需要注意DST类型的使用限制。
3. 错误写法的原因
Container<MyTrait>:MyTrait是DST,违反T: Sized约束,直接报错。Box<Container<MyTrait>>:Container<MyTrait>本身是DST,虽然Box可以包裹DST,但Container的T: Sized约束导致Container<MyTrait>根本无法实例化,因此报错。Box<Container<Box<MyTrait>>>:语法上少了一个闭合>,且这个写法冗余,不如直接返回Container<Box<dyn MyTrait>>简洁,若报错大概率是Serialize约束未满足。
内容的提问来源于stack exchange,提问作者Avba
相关产品推荐
相关产品推荐

