Rust中需保留TypePath结构,能否为str实现Deserialize trait?
问题
我正在维护一个现有Rust代码库,需要对包含< 'a, str>类型的TypePath结构体进行序列化/反序列化操作。以下是最小复现代码片段:
代码文件
main.rs
use serde::{Deserialize, Serialize}; use crate::cou::Cou; #[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] pub struct TypePath<'a>(Vec<Cou<'a, str>>);
cou.rs
use std::{ borrow::Borrow, sync::Arc, }; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub enum Cou<'a, T> where T: ToOwned + ?Sized, { Borrowed(&'a T), Upgraded(Arc<T::Owned>), }
编译错误
error[E0277]: the trait bound `str: Deserialize<'_>` is not satisfied --> src/ast.rs:136:25 | 136 | pub struct TypePath<'a>(Vec<Cou<'a, str>>); | ^^^ the trait `Deserialize<'de>` is not implemented for `str` | = help: the trait `Deserialize<'de>` is implemented for `&'a str` note: required for `Cou<'a, str>` to implement `Deserialize<'_>` --> src/cou.rs:11:21 | 11 | #[derive(Serialize, Deserialize)] | ^^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro 12 | pub enum Cou<'a, T> | ^^^^^^^^^^ = note: 1 redundant requirement hidden = note: required for `Vec<Cou<'a, str>>` to implement `Deserialize<'_>` = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
已知&'a str已实现Deserialize trait,但TypePath在代码库中被广泛使用,不想修改它,请问是否可以为str实现Deserialize trait?
解答
不能直接为
str实现Deserializetrait。Rust的孤儿规则规定,你只能为以下情况的类型实现外部trait:要么类型是你自己定义的,要么trait是你自己定义的。str是标准库类型,Deserialize是serde库提供的trait,两者都不属于你的 crate,因此无法直接为str实现该trait。替代方案:手动为
Cou<'a, str>实现Deserializetrait,绕过derive宏自动生成的约束。这样既不需要修改TypePath,也能满足反序列化需求:
use serde::de::{self, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; use std::{ borrow::Borrow, fmt, sync::Arc, }; #[derive(Serialize)] pub enum Cou<'a, T> where T: ToOwned + ?Sized, { Borrowed(&'a T), Upgraded(Arc<T::Owned>), } // 仅为Cou<'a, str>手动实现Deserialize impl<'de, 'a> Deserialize<'de> for Cou<'a, str> { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { struct CouVisitor<'a>; impl<'de, 'a> Visitor<'de> for CouVisitor<'a> { type Value = Cou<'a, str>; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a string") } // 处理借用的字符串 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: de::Error, { Ok(Cou::Borrowed(v)) } // 处理拥有所有权的字符串,转为Upgraded变体 fn visit_string<E>(self, v: String) -> Result<Self::Value, E> where E: de::Error, { Ok(Cou::Upgraded(Arc::new(v))) } } deserializer.deserialize_str(CouVisitor) } }
- 该实现会根据反序列化器提供的字符串形式,自动生成对应的
Cou变体:如果是借用的&str则生成Borrowed,如果是拥有所有权的String则转为Arc<String>生成Upgraded,完全匹配原枚举的语义,同时无需改动TypePath的定义。
内容的提问来源于stack exchange,提问作者diviquery
相关产品推荐
相关产品推荐

