Bob*_*Bob 7 functional-programming rust
我正在学习Rust,并想知道如何改进下面的代码.
我有一个形式元组的向量(u32, String).该u32值代表行号和Strings为在相应的行中的文本.只要所有String值都可以成功解析为整数,我想返回一个Ok<Vec<i32>>包含刚刚解析的String值,但如果不是,我想返回某种形式的错误(只是Err<String>在下面的例子中).
我正在努力学习避免可变性并在适当的地方使用功能样式,如果只是需要的话,上面的功能很简单.这是我在这种情况下想出的:
fn data_vals(sv: &Vec<(u32, String)>) -> Result<Vec<i32>, String> {
sv.iter()
.map(|s| s.1.parse::<i32>()
.map_err(|_e| "*** Invalid data.".to_string()))
.collect()
}
Run Code Online (Sandbox Code Playgroud)
但是,小问题是我想为每个无效值(而不仅仅是第一个)打印错误消息,并且错误消息应包含违规元组中的行号和字符串值.
我已设法使用以下代码执行此操作:
fn data_vals(sv: &Vec<(u32, String)>) -> Result<Vec<i32>, String> {
sv.iter()
.map(|s| (s.0, s.1.parse::<i32>()
.or_else(|e| {
eprintln!("ERROR: Invalid data value at line {}: '{}'",
s.0, s.1);
Err(e)
})))
.collect::<Vec<(u32, Result<i32, _>)>>() // Collect here to avoid short-circuit
.iter()
.map(|i| i.1
.clone()
.map_err(|_e| "*** Invalid data.".to_string()))
.collect()
}
Run Code Online (Sandbox Code Playgroud)
这有效,但看起来相当混乱和繁琐 - 特别是collect()在中间键入以避免短路,因此所有错误都被打印出来.这个clone()调用也很烦人,而且我不确定为什么需要它 - 编译器说我正在逐渐退出借来的内容,但我不确定是什么被移动了.有没有办法可以更干净地完成?或者我应该回到更程序化的风格?当我尝试时,我最终得到了可变变量和一个表示成功和失败的标志,这看起来不那么优雅:
fn data_vals(sv: &Vec<(u32, String)>) -> Result<Vec<i32>, String> {
let mut datavals = Vec::new();
let mut success = true;
for s in sv {
match s.1.parse::<i32>() {
Ok(v) => datavals.push(v),
Err(_e) => {
eprintln!("ERROR: Invalid data value at line {}: '{}'",
s.0, s.1);
success = false;
},
}
}
if success {
return Ok(datavals);
} else {
return Err("*** Invalid data.".to_string());
}
}
Run Code Online (Sandbox Code Playgroud)
有人可以告诉我最好的方法吗?我应该坚持这里的程序风格,如果可以的话,可以改进吗?或者有更清洁的功能性方法吗?还是两者的混合?任何建议表示赞赏
在纯粹的函数式风格中,您必须避免副作用。打印错误是一种副作用。首选样式是返回以下样式的对象:
Result<Vec<i32>, Vec<String>>
Run Code Online (Sandbox Code Playgroud)
并在函数返回后打印列表data_vals。
因此,本质上,您希望处理收集整数列表和字符串列表:
fn data_vals(sv: &Vec<(u32, String)>) -> Result<Vec<i32>, Vec<String>> {
let (ok, err): (Vec<_>, Vec<_>) = sv
.iter()
.map(|(i, s)| {
s.parse()
.map_err(|_e| format!("ERROR: Invalid data value at line {}: '{}'", i, s))
})
.partition(|e| e.is_ok());
if err.len() > 0 {
Err(err.iter().filter_map(|e| e.clone().err()).collect())
} else {
Ok(ok.iter().filter_map(|e| e.clone().ok()).collect())
}
}
fn main() {
let input = vec![(1, "0".to_string())];
let r = data_vals(&input);
assert_eq!(r, Ok(vec![0]));
let input = vec![(1, "zzz".to_string())];
let r = data_vals(&input);
assert_eq!(r, Err(vec!["ERROR: Invalid data value at line 1: 'zzz'".to_string()]));
}
Run Code Online (Sandbox Code Playgroud)
这使用partition不依赖于外部板条箱。
| 归档时间: |
|
| 查看次数: |
136 次 |
| 最近记录: |