rust 中的独立类型指针数组
Posted
技术标签:
【中文标题】rust 中的独立类型指针数组【英文标题】:Array of independent typed pointers in rust 【发布时间】:2021-10-07 02:55:06 【问题描述】:我有一个使用类型参数的自定义结构,例如:
struct Foo<'a, T: ?Sized>
parser: &'a dyn Fn(&str) -> Box<T>,
value: Option<Box<T>>
正如T: ?Sized
所示,Foo
类型元素的大小不会随T
的大小而变化(感谢Box
es)。
我想将多个Foo
元素放入一个数组中,这些元素可以有不同的T
,并将其传递给一个函数。
为此,我尝试了一些类似的方法:
fn parse_all<'a, T>(args: &mut [&mut Foo<'a, T>]) where T: Any;
fn main()
let a: Foo<i32> = Fooparser = ..., value = None;
let b: Foo<String> = Fooparser = ..., value = None;
parse_all(&mut[&mut a, &mut b]);
当然会失败,因为 a
和 b
有不同的 T
类型。
这个想法是我不需要知道数组元素的确切类型,因为Foo::parser
函数指针会处理它们。此外,它们的大小都是恒定的,所以我不应该有大小问题。
我是否可以通过使用Foo
绕过数组的每个元素具有完全相同且具有不同实际类型的a
和b
的事实? (例如在 C 中,我们可以将 void*
转换为任何指针类型)
顺便说一下,数组可以是任意大小,所以据我所知,我不能使用元组。
【问题讨论】:
请注意,您对Foo<T>
大小的评论不完整。类型不会随 T 的 size 变化,但仍会随 T 的 type 而变化,例如 Foo<String>
与 Foo<dyn std::fmt::Display>
。 play.rust-lang.org/…
【参考方案1】:
创建数组的问题不在于每个Foo<T>
会有不同的大小 - 正如您指出的那样,它们将是相同的。然而,每一个都可能有不同的 type,这是需要以某种方式存储的额外信息,Rust 不支持数组。
由于您似乎并不关心解析过程中生成的类型是什么,而不是Foo
中的泛型参数,您可以使用Box<dyn Any>
,然后仅在提取结果时向下转换:
struct Foo<'a>
parser: &'a dyn Fn(&str) -> Box<dyn Any>,
value: Option<Box<dyn Any>>,
impl Foo<'_>
fn try_extract<T: 'static>(&mut self) -> Option<Box<T>>
if let Some(val) = self.value.take()
match val.downcast::<T>()
Ok(x) => Some(x),
Err(x) =>
self.value = Some(x);
None
else
None
在您的代码中,您没有将任何字符串传递给 parse_all
,但我看不出它可以完成什么,所以我为工作示例稍微修改了它:playground
请注意,如果 T
没有大小,这将不起作用,因为 Box::downcast
需要 T: Sized
。
您还需要以某种方式独立于解析器数组存储要解码的类型,但是如果不查看更多代码,很难提出一个好的方法来做到这一点。
【讨论】:
非常感谢您的回答和付出的努力。您对parse_all
应该做什么假设得很好,我可能删除了太多代码。在我的情况下,T
的大小不应该太小,所以我可以接受。我的Foo
元素通常是手工创建的,因为你帮助我的将是我正在做的一个小参数解析库的 api 的一部分。就我而言,@Frxstrem 解决方案更合适,所以我会改用它,但您的解决方案仍然很棒!【参考方案2】:
数组的所有元素必须属于同一类型。但是,该类型可以是 trait 对象,例如 Box<dyn FooTrait>
或 &mut dyn FooTrait
用于自定义 trait FooTrait
。
您可以为您的所有Foo
类型定义特征并实现它,并使用一些您可以在不知道具体类型T
的情况下调用的通用接口:
trait FooTrait
fn do_something(&mut self);
impl<'a, T> FooTrait for Foo<'a, T>
fn do_something(&mut self)
unimplemented!()
因为&mut T
如果T: FooTrait
自动转换为&mut dyn FooTrait
,您可以在函数签名中使用它:
fn parse_all(args: &mut [&mut dyn FooTrait])
// ^^^^^^^^^^^^
for arg in args
arg.do_something();
fn main()
let mut a: Foo<i32> = Fooparser = ..., value = None;
let mut b: Foo<String> = Fooparser = ..., value = None;
parse_all(&mut[&mut a, &mut b]);
如果所有Foo<T>
的大小都相同,这并不重要,因为只有引用(总是相同的大小)存储在数组中。因此,如果您想避免堆分配,您也可以在 Foo
结构中删除 Box
的使用:
struct Foo<'a, T>
parser: &'a dyn Fn(&str) -> T,
value: Option<T>
【讨论】:
以上是关于rust 中的独立类型指针数组的主要内容,如果未能解决你的问题,请参考以下文章