如何使 Rust Generic Struct/Trait 需要 Box<other trait>?

Posted

技术标签:

【中文标题】如何使 Rust Generic Struct/Trait 需要 Box<other trait>?【英文标题】:How to make a Rust Generic Struct/Trait require a Box<other trait>? 【发布时间】:2021-10-01 16:40:17 【问题描述】:

我有一个特征Agent 代表模拟中的代理,还有一个结构SimpleAgent 实现了这个特征。由于Agent 的大小在编译时是未知的,所以我的代码一般使用Vec&lt;Box&lt;dyn Agent&gt;&gt; 我想创建一个通用特征AgentCollection&lt;T&gt; 并使用AgentTree&lt;T&gt; 结构实现它。

到目前为止,我有以下内容:

pub trait AgentCollection<T> 
    fn new(agents: Vec<Box<T>>) -> Self;
    fn get_in_rectilinear_range(point: vec::Vec2, range: f64) -> Vec<Box<T>>;
    fn get_in_euclidean_range(point: vec::Vec2, range: f64) -> Vec<Box<T>>;


pub struct AgentTree<T: agent::Agent> 
    left: Option<Box<AgentTree<T>>>,
    right: Option<Box<AgentTree<T>>>,
    node: Box<T>,


#[allow(unused)]
impl<T: agent::Agent> AgentTree<T> 
    fn range_search(point: vec::Vec2, range: f64) -> std::vec::Vec<Box<T>> 
        todo!()
    


impl<T: agent::Agent> AgentCollection<T> for AgentTree<T> 
    fn new(agents: std::vec::Vec<Box<T>>) -> Self 
        todo!()
    

    fn get_in_rectilinear_range(point: vec::Vec2, range: f64) -> std::vec::Vec<Box<T>> 
        todo!()
    

    fn get_in_euclidean_range(point: vec::Vec2, range: f64) -> std::vec::Vec<Box<T>> 
        todo!()
    

这都是类型检查。但是,当我在我的主文件中使用它时,例如

let agent_tree = AgentTree::new(last_agents);

last_agents 的类型为 std::vec::Vec&lt;std::boxed::Box&lt;dyn agent::Agent&gt;&gt;,我收到错误 the size for values of type 'dyn agent::Agent' cannot be known at compilation time

我想我想以某种方式将AgentTree 类型参数限制为Box&lt;agent::Agent 而不仅仅是agent::Agent,以便调整大小,但我不知道该怎么做。例如,我尝试过:pub struct AgentTree&lt;T: Box&lt;agent::Agent&gt;&gt; ...

【问题讨论】:

【参考方案1】:

默认情况下,泛型类型参数将有一个Sized trait 绑定,它需要一个大小的类型。但是像dyn Agent 这样的特征对象没有大小,它们可以表示多种类型,因此在编译时无法知道它们的大小。

要使您的代码与 dyn Agent 一起工作,您只需在 trait bound 中添加 ?Sized 以删除默认的 Sized trait bound:

pub trait AgentCollection<T: ?Sized> 
//                           ^^^^^^
    fn new(agents: Vec<Box<T>>) -> Self;
    fn get_in_rectilinear_range(point: vec::Vec2, range: f64) -> Vec<Box<T>>;
    fn get_in_euclidean_range(point: vec::Vec2, range: f64) -> Vec<Box<T>>;


pub struct AgentTree<T: ?Sized + agent::Agent> 
//                      ^^^^^^
    left: Option<Box<AgentTree<T>>>,
    right: Option<Box<AgentTree<T>>>,
    node: Box<T>,


#[allow(unused)]
impl<T: ?Sized + agent::Agent> AgentTree<T> 
//      ^^^^^^
    fn range_search(point: vec::Vec2, range: f64) -> std::vec::Vec<Box<T>> 
        todo!()
    


impl<T: ?Sized + agent::Agent> AgentCollection<T> for AgentTree<T> 
//      ^^^^^^
    fn new(agents: std::vec::Vec<Box<T>>) -> Self 
        todo!()
    

    fn get_in_rectilinear_range(point: vec::Vec2, range: f64) -> std::vec::Vec<Box<T>> 
        todo!()
    

    fn get_in_euclidean_range(point: vec::Vec2, range: f64) -> std::vec::Vec<Box<T>> 
        todo!()
    

【讨论】:

【参考方案2】:

想通了,我添加了以下内容:

type BoxedAgent = Box<dyn agent::Agent>;

然后用BoxedAgent 代替Box&lt;T: agent::Agent&gt;

打算暂时搁置一下,以防有人有更好的建议。

【讨论】:

以上是关于如何使 Rust Generic Struct/Trait 需要 Box<other trait>?的主要内容,如果未能解决你的问题,请参考以下文章

Rust traits act as generic constraints

如何使 Rust 可变引用不可变?

如何使我的 Rust 函数更通用和高效?

如何使 Rust Game of Life WebAssembly 作为静态网站运行?

Rust如何尊重复制特性?

如何“使用”或导入本地 Rust 文件? [复制]