在我的简短Rust经历中,我多次遇到这种模式,我不确定我解决它的方式是否足够......
让我们假设我有一些看起来像这样的特征:
trait Container {
type Item;
fn describe_container() -> String;
}
Run Code Online (Sandbox Code Playgroud)
还有一些实现这个特性的结构:
struct ImAContainerType;
struct ImAnItemType;
impl Container for ImAContainerType {
type Item = ImAnItemType;
fn describe_container() -> String { "some container that contains items".to_string() }
}
Run Code Online (Sandbox Code Playgroud)
这可能是一个容器,它知道它包含的项目类型,如本例所示,或者,作为另一个例子,知道应该返回什么类型的响应的请求等.
现在我发现自己处于某种情况,当我需要实现一个带有项(相关类型)的函数并调用容器的静态函数(父特征)时.这是第一次天真的尝试:
fn describe_item_container<C: Container>(item: C::Item) -> String {
C::describe_container()
}
Run Code Online (Sandbox Code Playgroud)
这不会编译,因为关联类型不是单射的,并且Item可能有几个可能的Containers,所以这整个情况是模糊的.我需要以某种方式提供实际Container类型,但不提供任何容器数据.当我调用这个函数时,我可能根本没有容器数据本身!
在寻找解决方案时,我找到了std :: marker :: PhantomData的文档.它说:
PhantomData允许您描述类型的行为就像存储类型T的值一样,即使它没有.
这必须是Rust替代Haskell的Proxy类型,对吧?我们试着用它:
fn describe_item_container<C: Container>(container: PhantomData<C>, item: C::Item) -> String {
C::describe_container()
}
let s = describe_item_container(PhantomData::<PhantomData<ImAContainerType>>, ImAnItemType);
println!("{}", s);
Run Code Online (Sandbox Code Playgroud)
正在编译...错误:
error[E0277]: the trait bound `std::marker::PhantomData<ImAContainerType>: Container` is not satisfied
Run Code Online (Sandbox Code Playgroud)
我问#rust-beginners并得到回应:PhantomData根本不应该以这种方式使用!另外,我有一个建议,只是让从一个落后的相关类型的链接Item来Container.像这样的东西:
trait Item {
type C: Container;
}
fn describe_item_container<I: Item>(item: I) -> String {
I::C::describe_container()
}
Run Code Online (Sandbox Code Playgroud)
它应该工作,但使事情变得更加复杂(特别是对于物品可以放在不同的容器种类的情况下)......
经过更多的实验,我做了以下更改,所有内容都编译并正常工作:
let s = describe_item_container(PhantomData::<ImAContainerType>, ImAnItemType);
println!("{}", s);
Run Code Online (Sandbox Code Playgroud)
这种变化是::<PhantomData<ImAContainerType>>对::<ImAContainerType>.
它有效,但现在我完全糊涂了.这是正确的使用方法PhantomData吗?为什么它可以工作?是否有其他更好的方法为Rust中的函数提供仅类型参数?
编辑:我的例子中有一些过于简单化,因为在特定情况下,调用会更容易ImAContainerType::describe_container().这是一个更复杂的情况,当函数实际上用a做某事时Item,仍然需要容器类型信息.
如果要将类型参数传递给函数,则可以执行此操作.您不必将其遗漏以进行推断.
这是它寻找你的第二个例子(playground)的方式:
fn pack_item<C: Container>(item: C::Item) -> ItemPacket {
ItemPacket {
container_description: C::describe_container(),
_payload: item.get_payload(),
}
}
fn main() {
let s = pack_item::<ImAContainerType>(ImAnItemType);
println!("{}", s.container_description);
let s = pack_item::<ImAnotherContainerType>(ImAnItemType);
println!("{}", s.container_description);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
450 次 |
| 最近记录: |