我正在编写一个在成功(和失败)时返回serde_json :: Value的函数。以前在Rust中,我一直省略分号来从函数返回数据,如下面的代码示例所示:
use serde_json::{Result, Value};
use core::result::Result as ResultCore;
fn returning_function() -> ResultCore<Value, Value> {
let data = r#"
{
"status": "ok",
"response": {
"data": "secret message!"
}
}
"#;
match str_to_json(data) {
Ok(json_data) => match json_data["status"].as_str() {
Some(status_str) => {
if status_str == "ok" {
Ok(json_data["response"].clone())
}
}
None => eprintln!("\"status\" was not a string")
}
Err(error) => eprintln!("something went wrong! here's what: {}", error)
}
Err(serde_json::Value::Null)
}
fn str_to_json(json_data: &str) -> Result<Value> {
Ok(serde_json::from_str(json_data)?)
}
这是我不了解的部分:这无法编译。 Rust的编译器告诉我“类型不匹配”,expected type ()
,但found type serde_json::value::Value
。现在,我找到了可以编译的解决方案,如下所示:
use serde_json::{Result, Value};
use core::result::Result as ResultCore;
fn returning_function() -> ResultCore<Value, Value> {
let data = r#"
{
"status": "ok",
"response": {
"data": "secret message!"
}
}
"#;
match str_to_json(data) {
Ok(json_data) => match json_data["status"].as_str() {
Some(status_str) => {
if status_str == "ok" {
return Ok(json_data["response"].clone());
// ^ added return statement here
}
}
None => eprintln!("\"status\" was not a string")
}
Err(error) => eprintln!("something went wrong! here's what: {}", error)
}
Err(serde_json::Value::Null)
}
fn str_to_json(json_data: &str) -> Result<Value> {
Ok(serde_json::from_str(json_data)?)
}
通过添加return
语句,编译器突然感到很高兴,并且编译器再也无话可说了。为什么是这样?我的印象是,省略分号并使用return语句具有相同的含义-为什么在这里有区别?
答案 0 :(得分:4)
return
语句(也称为早期返回)将从最后/最内层函数式范围返回对象。 (类似函数,因为它同时适用于闭包和函数)
let x = || {
return 0;
println!("This will never happen!");
};
fn foo() {
return 0;
println!("This won't happen either");
}
缺席的分号将代替return
那样计算表达式,但只会返回到最后一个/最里面的范围,换句话说,它将从{}
的任何集合中返回。
let x = { // Scope start
0
}; // Scope end
fn foo() -> usize { // Scope start
0
} // Scope end
return
语句将突破任何数量的嵌套作用域,直到达到类似函数的作用域为止:
fn foo() -> usize {// <------------------------------------------\
{ // |
{ // |
{ // |
{ // |
{ // |
{ // |
{ // |
{ // |
{ // |
{ // |
{ // |
return 0; // ---/
}
}
}
}
}
}
}
}
}
}
}
}
return
语句也有它自己的类型,也就是说,let x = return;
实际上会编译。
return语句的计算结果为!
,也就是the never type。您现在无法在稳定的锈蚀中命名它,但最终它将变得稳定且可用。
答案 1 :(得分:1)
如The Book中所述:
在Rust中,函数的返回值与函数主体块中最终表达式的值同义。
换句话说-表达式没有返回值的分号不是事实,而是函数中的最终表达式。分号用于分隔表达式,因此:
fn foo() -> i32 {
5;
}
等于产生值5的表达式,后跟一个不产生任何结果的空表达式。因此上面的函数将无法编译。
如果要在到达最终表达式之前从函数返回,return
关键字很方便。这就是您要在示例中尝试的操作。
还要注意,所有潜在的返回值必须与函数本身的返回值具有相同的类型。
以上内容均未完全说明您遇到的编译器错误。您的内部匹配看起来像这样:
match json_data["status"].as_str() {
Some(status_str) => {
if status_str == "ok" {
Ok(json_data["response"].clone())
}
}
None => eprintln!("\"status\" was not a string")
}
匹配块的规则之一是每个手臂必须求出相同的类型。但是在上述情况下,一只手臂的求值结果可能为std::result::Result<serde_json::value::Value, _>
,而另一只手臂的求值结果为空(或更准确地说,是空值()
)。
插入return
可以避免该错误,因为Some
臂现在完全从函数返回,而不是求值为std::result::Result<serde_json::value::Value, _>
类型的值。