我编写了一个实用程序方法,该方法可以从TOML文件创建配置对象:
pub fn read_common(path: &str) -> Result<Config> {
let content = read_file(path);
toml::from_str(&content?).map_err(|e| {
Error::new(
ErrorKind::InvalidData,
format!(
"Parsing failed at line {:?}, col {:?}",
e.line_col().map(|p| p.0),
e.line_col().map(|p| p.1)
),
)
})
}
Config
是一个简单的可反序列化的结构:
#[derive(Deserialize)]
pub struct DatabaseConfig {
pub host: String,
pub port: u16,
pub database: String,
pub user: String,
pub password: String,
}
#[derive(Deserialize)]
pub struct Config {
pub database: DatabaseConfig,
}
这可以编译并正常运行(应该这样做,因为它几乎是从the toml crate's documentation复制粘贴而来的。)
我有多个配置文件,每个配置文件都具有对应的匹配结构,因此我想泛化我的read_common
方法(在所有其他情况下重用相同的错误处理)。
我写了这个:
pub fn read<'de, T>(path: &str) -> Result<T>
where
T: Deserialize<'de>,
{
let content = read_file(path);
toml::from_str(&content?).map_err(|e| {
Error::new(
ErrorKind::InvalidData,
format!(
"Parsing failed at line {:?}, col {:?}",
e.line_col().map(|p| p.0),
e.line_col().map(|p| p.1)
),
)
})
}
T
是反序列化产生的配置类型。我需要将其扩展Deserialize<'..>
,所以我需要在此处给出生命周期,但是toml::from_str
的完整签名是
pub fn from_str<'de, T>(s: &'de str) -> Result<T, Error>
where
T: de::Deserialize<'de>,
据我了解,我的输入字符串的寿命应与产生的反序列化对象的寿命相同(或更长)。自从它的生命周期在我的read
方法结束时结束以来,它当然不在我的实现中。因此,借用检查器会抱怨(borrowed value does not live long enough on &content?
,并且此实现无法编译。
我如何编写适用于多种输出类型的read
的通用read_common
版本?
我对此问题还有其他疑问:
read_common
中的生存期会怎样?反序列化的字符串也是短暂的,该实现如何编译?我想#[derive(Deserialize)]
中隐藏了一些技巧。toml::from_str
的输入变量和输出类型的寿命相同?对我来说,反序列化完成后,输出不应引用输入字符串,为什么方法声明中存在生命周期?