我想制作一个行为类似于String
的“受约束”类型,但是在构造上可以验证它是否符合某些语法。例如,我们可以创建一个Identifier
类型,其行为类似于String
,但强制所有字符都与[a-zA-Z_]
匹配。
尤其是,我希望实现通常的特征(Display
,Ord
等),并且我希望serde::Serialize
和serde::Deserialize
的行为与正常的String
,只是我们在反序列化时会进行验证。
是否有惯用的方式做到这一点,还是我必须手动实现所有特征?
答案 0 :(得分:3)
惯用的方法是在String
周围创建一个newtype并导出可以导出的特征。
use serde;
use serde_json;
#[derive(
Eq, PartialEq, Ord, PartialOrd,
Debug, Default,
serde::Serialize, serde::Deserialize,
)]
#[serde(transparent)] // to serialize as a string
pub struct Identifier(
#[serde(deserialize_with = "Identifier::deserialize")]
String
);
impl Identifier {
pub fn new(s: String) -> Result<Identifier, &'static str> {
// do some validation
if !s.is_empty() && s.contains('a') {
Ok(Identifier(s))
} else {
Err("Not valid")
}
}
fn deserialize<'de, D>(d: D) -> Result<String, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
use serde::Deserialize;
let s = String::deserialize(d)?;
Identifier::new(s).map(|i| i.0).map_err(D::Error::custom)
}
}
fn main() {
let a = Identifier::new("aaa".into()).unwrap();
let b: Identifier = serde_json::from_str(r#""faaa""#).unwrap();
println!("{:?}, {:?}", a, b);
println!("{}", a == b);
}