将 'key exists' 与 'and if its the right type' 解析 toml 结合的惯用方法
idiomatic way to combine 'key exists' with 'and if its the right type' parsing toml
我正在解析这个
[xxxxx]
drive0={}
drive1={path="xxxx"}
...
有时有路径,有时没有。
我有工作代码,但我仍在努力学习 Rust 惯用的做事方式。代码:
for i in 0..8 {
let drive_name = format!("drive{}", i);
if dmap.contains_key(&drive_name) {
if let Some(d) = config[drive_name].as_table() {
this.units.push(Rkunit::new(true));
if d.contains_key("path") {
if let Some(path) = d["path"].as_str() {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)
.unwrap();
this.units[i].file.replace(file);
}
}
} else {
this.units.push(Rkunit::new(false));
}
}
}
我预计
if let Some(path) = d["path"].as_str()
(即没有 if d.contains()
行)
会处理这两种情况——即没有“路径”和“路径”不是字符串,但事实并非如此。 contains_key(drive_name)
也一样。
我尝试了各种猜测语法,看看是否可以避免另一个嵌套 if 并找到一个。
那么有没有更好的方法,或者说这已经很好了。欢迎对解析 toml 提出任何其他意见。
这里有一些方法可能是有效的。由于您的代码非常复杂并且使用 non-std API,因此很难看出我的更改是否有用:
使用您的通用代码结构,但组合 .contains
并将包含的值应用到模式 .get(...).map(...)
中。 x.get(y)
returns 一个选项值,它允许您访问整个选项 API,不像 x[y]
如果密钥不存在会出现恐慌。
if let Some(d) = config.get(&drive_name).map(|c| c.as_table()) {
this.units.push(Rkunit::new(true);
if let Some(path) = d.get("path").and_then(String::as_str) {
}
} else {
this.units.push(Rkunit::new(false));
}
您可以对某些 pre-work 使用匹配语句。我个人更喜欢这个,因为它使匹配武器非常明确,但我认为它不那么地道:
let drive = config.get(&driver_name); // return an option
let path = drive.map(|d|.get("path")); // returns an option
match (drive, path) {
(Some(d), Some(p)) => {
this.units.push(Rkunit::new(true));
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)
.unwrap();
this.units[i].file.replace(p);
}
(Some(d), None) => {
this.units.push(Rkunit::new(true);
}
_ => {
this.units.push(Rkunit::new(false);
}
}
我认为 1. 更符合惯用语,但我当然都看过,而且可能更像是一种风格问题。组合选项当然是惯用的包含和访问。
有人可能认为选项链更符合惯用语,但更难理解。
config.get(&driver_name)
.or_else(|| { // no drive, passing None all the way down
this.units.push(Rkunit::new(false));
None
})
.and_then(|drive| { // having a drive, trying to get a path
this.units.push(Rkunit::new(true));
drive.as_table().get("path")
})
.map(|path| { // only having a path, we're doing the thing
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path.as_str()) // as_str is there
.unwrap();
this.units[i].file.replace(file);
});
// also "unused Option" warning, because map returns an Option<()>
基于对 somnium 回答的轻微按摩,我以此结束。感觉更清脆,我得学习更多地道的 rust
for i in 0..8 {
let drive_name = format!("drive{}", i);
if let Some(drive) = dmap.get(&drive_name).and_then(|x| x.as_table()) {
this.units.push(Rkunit::new(true));
if let Some(path) = drive.get("path").and_then(|x| x.as_str()) {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)
.unwrap();
this.units[i].file.replace(file);
}
} else {
this.units.push(Rkunit::new(false));
}
}
我知道任何配置错误都会被忽略。但这就是我所追求的。如果给出非法路径,可能不应该爆炸 -> 稍后
我正在解析这个
[xxxxx]
drive0={}
drive1={path="xxxx"}
...
有时有路径,有时没有。
我有工作代码,但我仍在努力学习 Rust 惯用的做事方式。代码:
for i in 0..8 {
let drive_name = format!("drive{}", i);
if dmap.contains_key(&drive_name) {
if let Some(d) = config[drive_name].as_table() {
this.units.push(Rkunit::new(true));
if d.contains_key("path") {
if let Some(path) = d["path"].as_str() {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)
.unwrap();
this.units[i].file.replace(file);
}
}
} else {
this.units.push(Rkunit::new(false));
}
}
}
我预计
if let Some(path) = d["path"].as_str()
(即没有 if d.contains()
行)
会处理这两种情况——即没有“路径”和“路径”不是字符串,但事实并非如此。 contains_key(drive_name)
也一样。
我尝试了各种猜测语法,看看是否可以避免另一个嵌套 if 并找到一个。
那么有没有更好的方法,或者说这已经很好了。欢迎对解析 toml 提出任何其他意见。
这里有一些方法可能是有效的。由于您的代码非常复杂并且使用 non-std API,因此很难看出我的更改是否有用:
使用您的通用代码结构,但组合
.contains
并将包含的值应用到模式.get(...).map(...)
中。x.get(y)
returns 一个选项值,它允许您访问整个选项 API,不像x[y]
如果密钥不存在会出现恐慌。if let Some(d) = config.get(&drive_name).map(|c| c.as_table()) { this.units.push(Rkunit::new(true); if let Some(path) = d.get("path").and_then(String::as_str) { } } else { this.units.push(Rkunit::new(false)); }
您可以对某些 pre-work 使用匹配语句。我个人更喜欢这个,因为它使匹配武器非常明确,但我认为它不那么地道:
let drive = config.get(&driver_name); // return an option let path = drive.map(|d|.get("path")); // returns an option match (drive, path) { (Some(d), Some(p)) => { this.units.push(Rkunit::new(true)); let file = OpenOptions::new() .read(true) .write(true) .create(true) .open(path) .unwrap(); this.units[i].file.replace(p); } (Some(d), None) => { this.units.push(Rkunit::new(true); } _ => { this.units.push(Rkunit::new(false); } }
我认为 1. 更符合惯用语,但我当然都看过,而且可能更像是一种风格问题。组合选项当然是惯用的包含和访问。
有人可能认为选项链更符合惯用语,但更难理解。
config.get(&driver_name)
.or_else(|| { // no drive, passing None all the way down
this.units.push(Rkunit::new(false));
None
})
.and_then(|drive| { // having a drive, trying to get a path
this.units.push(Rkunit::new(true));
drive.as_table().get("path")
})
.map(|path| { // only having a path, we're doing the thing
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path.as_str()) // as_str is there
.unwrap();
this.units[i].file.replace(file);
});
// also "unused Option" warning, because map returns an Option<()>
基于对 somnium 回答的轻微按摩,我以此结束。感觉更清脆,我得学习更多地道的 rust
for i in 0..8 {
let drive_name = format!("drive{}", i);
if let Some(drive) = dmap.get(&drive_name).and_then(|x| x.as_table()) {
this.units.push(Rkunit::new(true));
if let Some(path) = drive.get("path").and_then(|x| x.as_str()) {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)
.unwrap();
this.units[i].file.replace(file);
}
} else {
this.units.push(Rkunit::new(false));
}
}
我知道任何配置错误都会被忽略。但这就是我所追求的。如果给出非法路径,可能不应该爆炸 -> 稍后