是否存在与?
快捷方式类似的内容,而不是在发生错误时从函数返回结果,返回预定义的值?
基本上我想知道是否可以在一行中执行以下操作:
fn index() -> String {
let temp = some_func("pass"); // some_func returns a Result
if temp.is_err() {
return "No parameters named pass".to_string();
}
try_decrypt_data(temp.unwrap())
}
我尝试使用unwrap_or_else()
,但这只是返回闭包而不是外部函数。即。
try_decrypt_data(params.get("pass").unwrap_or_else(|| return "No parameters named pass".to_string(); )) // This doesn't work
答案 0 :(得分:4)
这有点可能,但通常不是一个好主意,特别是在你的例子中(我稍后会解释)。
您无法轻松返回String
并使?
返回默认值,但您可以定义自己的字符串类型并为其实现std::ops::Try
。请注意,Try
仍然不稳定!
让我们看看这是如何运作的:
// Just wrap a string
struct StringlyResult {
s: String,
}
// Convenience conversion
impl From<String> for StringlyResult {
fn from(s: String) -> Self {
Self { s }
}
}
// The impl that allows us to use the ? operator
impl std::ops::Try for StringlyResult {
type Ok = String;
type Error = String;
fn into_result(self) -> Result<Self::Ok, Self::Error> {
if self.s == "No parameters named pass" {
Err(self.s)
} else {
Ok(self.s)
}
}
fn from_error(s: Self::Error) -> Self {
if s != "No parameters named pass" {
panic!("wat");
}
Self { s }
}
fn from_ok(s: Self::Ok) -> Self {
if s == "No parameters named pass" {
panic!("wat");
}
Self { s }
}
}
我们可以像这样实现index()
:
fn index() -> StringlyResult {
let temp = some_func("pass")
.map_err(|_| "No parameters named pass")?;
try_decrypt_data(&temp).into()
}
(Complete code on the Playground)
所以是的,Try
特征使用户能够将?
运算符与他们自己的类型一起使用。
但是,如您的示例中所示,这是一个糟糕的主意。您可能已经注意到&#34; wat &#34;上面代码中的部分。问题是你的OK类型已经耗尽了整个类型(该类型的所有实例都是有效的OK实例)。
考虑一个函数get_file_size() -> u64
。现在这个功能可能会失败(即它无法确定文件大小)。您无法返回0
来表示发生了故障。函数的调用者如何区分函数无法确定文件大小的环境和文件实际为0字节大的环境?
同样,您的函数调用者如何区分发生错误的情况和解密文本字面上"No parameters named pass"
的情况?来电者不能!那太糟糕了。
请注意,有类似的东西,但并不是那么糟糕,但在Rust中仍然不是真正的惯用语:get_file_size() -> i64
。在这里,我们可以返回-1
来表示失败。这不太糟糕,因为-1
永远不会是有效的文件大小! (换句话说,并非所有类型的实例都是有效的OK实例)。但是,在这种情况下,忘记检查错误仍然非常容易。这就是为什么在Rust中,我们总是想使用Result
。
为了简化错误处理,请考虑使用the crate failure
。这样,您可以轻松地将字符串用作错误消息,而不会牺牲程序的类型安全性或健全性。例如:
use failure::{Error, ResultExt};
fn index() -> Result<String, Error> {
let temp = some_func("pass")
.context("No parameters named pass")?;
Ok(try_decrypt_data(&temp)?)
}
答案 1 :(得分:1)
没有这样的实用程序,但您始终可以编写宏,例如:
macro_rules! return_if_err {
( $to_test:expr, $default:expr ) => (
if $to_test.is_err() {
return $default;
}
)
}
fn pop_or_default(mut v: Vec<i32>) -> i32 {
let result = v.pop();
return_if_err!(result.ok_or(()), 123);
result.unwrap()
}
fn main() {
assert_eq!(pop_or_default(vec![]), 123);
}
您无法从闭包中返回外部作用域。
答案 2 :(得分:1)
我将创建一个使用Result
的内部函数。这样,您就可以将问号运算符用于要返回的各种错误消息/默认值。然后,您可以调用内部函数并获取成功值或错误值:
fn index() -> String {
fn inner() -> Result<String, String> {
let t1 = some_func("pass").map_err(|_| "No parameters named pass")?;
let t2 = some_func2(t1).map_err(|_| "A second error")?;
let t3 = some_func3(t2).map_err(|_| "A third error")?;
Ok(t3)
}
match inner() {
Ok(v) => v,
Err(v) => v,
}
}
有一个名为try blocks的不稳定功能,有望使它的体积减轻一些:
#![feature(try_blocks)]
fn index() -> String {
let v = try {
let t1 = some_func("pass").map_err(|_| "No parameters named pass")?;
let t2 = some_func2(t1).map_err(|_| "A second error")?;
some_func3(t2).map_err(|_| "A third error")?
};
match v {
Ok(v) => v,
Err(v) => v,
}
}