这是种可能的,但通常不是一个好主意,尤其是在您的示例中(我稍后会解释)。
您不能轻易返回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 特征使用户能够将? 运算符与他们自己的类型一起使用。
但是,如您的示例中所述,这是一个糟糕的想法。您可能已经注意到我上面代码中的“wat”部分。问题是您的 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)?)
}