我正在尝试将我设计的数据模型适应serde MapAccess
接口。我有自己的数据模型,它有一个包含键值对的List
类型,如下所示:
impl List {
fn next_item(&mut self) -> Option<(String, Item<'_>)>
}
换句话说,Item
类型可变地从List
对象借用,并且必须在从List
提取下一个Item
之前完全处理。
我需要使这个数据模型适应一个看起来像这样的反序列化器trait:
trait MapAccess {
fn next_key<T: Deserialize>(&mut self) -> Option<T>;
fn next_value<T: Deserialize>(&mut self) -> T;
}
这两个方法应交替调用;如果你没有按照正确的顺序调用它们,它们会被允许panic或者返回垃圾结果。这个trait是由我正在使用的反序列化器库提供的,也是这个问题中唯一一个我无法控制或修改的。
我试图将List
或&mut List
转换为MapAccess
,但遇到了一些问题。我确实有一个函数可以将Item<'_>
转换为T: Deserialize
(特别是结束List
的借用):
fn deserialize_item<T: Deserialize>(item: Item<'_>) -> T;
我遇到的基本问题是MapAccess::next_key
调用List::next_item
,然后将String反序列化为键,然后需要存储Item<'_>
,以便next_value
可以处理它。(两个字段都可变地借用相同的东西)。这不是真正的自我引用,所以我不是我最接近的是某种类似于enum State {List(&'a mut List), Item(Item<'_>)}
的枚举(因为我不需要访问一个List
,而我需要访问一个List
)。我正在使用Item
,但是我还没有找到一种方法,可以在Item
被使用后,为下一个next_key
恢复对List
的访问。
作为数据模型的作者,我确实有一些余地来改变它的设计方式,但是 * 如果可能的话 * 我想保留某种借用模型,我用它来加强解析的一致性(即借用防止X1 M22 N1 X解析下一项,直到X1 M23 N1 X完全解析当前项的内容之后。当然,我可以设计任何其他类型来正确地实现它。
注意:这些都是serde相关的,但是我使用原始类型来简化接口。特别是我省略了所有的Result
类型和'de
生存期,因为我相信它们与这个问题无关。
编辑:这是我的“尝试”来做一些有用的东西。这没有编译,但希望它能理解我想在这里做的事情。
fn deserialize_string<T: Deserialize>(value: String) -> T { todo!() }
fn deserialize_item<T: Deserialize>(item: Item<'_>) -> T { todo!() }
struct ListMapAccess<'a> {
list: &'a mut List,
item: Option<Item<'a>>,
}
impl<'a> MapAccess for ListMapAccess<'a> {
fn next_key<T: Deserialize>(&mut self) -> Option<T> {
let (key, item) = self.list.next_item()?;
self.item = Some(item);
Some(deserialize_string(key))
}
fn next_value<T: Deserialize>(&mut self) -> T {
let item = self.item.expect("Called next_item out of order");
deserialize_item(item)
}
}
现在,我真的完全不依赖于ListMapAccess
结构体;我对任何将我的List
/Item
接口集成到MapAccess
的解决方案都很满意。
1条答案
按热度按时间oprakyz71#
这似乎是不可能的安全生 rust ,但我已经想出了这个不安全的解决方案。
这里的关键是
ListMapAccess
必须拥有一个NonNull<List>
,而不是一个可变的引用。我们提供了一个API,它保证在编译时列表永远不会被可变地借用超过一次,因为它只允许在self.item
(固有地借用列表)为空时创建可变借用,并防止ListMapAccess
逃脱所有权。这里的代码是真实世界用例的简化(它避免了一堆与手头问题无关的额外生命周期和
Result
返回类型),但对于感兴趣的人来说,这里提供了真实世界版本。