在Rust中,unwrap_or需要使用String或者&str吗?

3
我正在尝试解析一个环境变量,使用如下的std::env
let environment = env::var("SENSIBULL_ENVIRONMENT").unwrap();

这将把一个字符串返回给变量environment。如果我想传递一个默认值,那么我必须使用下面的代码。
let environment = env::var("SENSIBULL_ENVIRONMENT").unwrap_or("development".into());

但是我期望像这样做。
let environment = env::var("SENSIBULL_ENVIRONMENT").unwrap_or("development");

如 Rust example 中所提到的
但是它显示错误 String expected but found &str 为什么在示例代码中没有发生这个错误呢?

这回答解决了你的问题吗?Rust中String和str有什么区别? - springworks00
1
最好使用.unwrap_or_else(|| "development".into()),这样如果环境变量存在,则不会分配默认字符串。 - Aloso
2个回答

5
在Rust中,字符串字面量的类型是&str,而环境变量的类型是String。实质上,&str具有固定的大小,而String可以动态改变和调整大小(更多信息请点击这里)。unwrap_or要求备选值与选项值具有相同的类型,这意味着您必须提供一个String以匹配环境变量的类型。这就是为什么您必须调用.into将其转换为一个字符串。在他们的示例中,选项使用了一个字符串字面量,它也是&str类型,因此没有错误。

1
在Rust中,字符串字面量的类型为&str,环境变量的类型为String。补充说明:envvars是String类型,因为Unix的getenv非常不安全,它返回指向“环境数据”缓冲区的指针,因此该指针可以在任何时刻失效。因此,Rust立即将该数据复制到一个拥有的缓冲区(一个OsString)中,并可选择解码为方便起见的“proper” String(即使不是有效的Unicode,env::var_os也提供对“raw”数据的访问)。 - Masklinn

1
如果您不需要对所拥有的字符串拥有所有权,您可以使用as_deref()将其转换为字符串切片。这样可以在unwrap_or中提供匹配的默认值作为(静态)字符串切片,以避免分配内存。
let environment = std::env::var("APP_ENV").as_deref().unwrap_or("development");

很遗憾,这个还不起作用,因为编译器太聪明了,在转换后它会提前丢弃所拥有的值,因此无法在转换后访问。但是编译器的错误信息很有帮助,建议使用单独的变量声明来防止这种情况发生。
error[E0716]: temporary value dropped while borrowed
 --> src/main.rs:2:23
  |
2 |     let environment = std::env::var("APP_ENV").as_deref().unwrap_or("development");
  |                       ^^^^^^^^^^^^^^^^^^^^^^^^                                    - temporary value is freed at the end of this statement
  |                       |
  |                       creates a temporary value which is freed while still in use
3 |     println!("{}", environment);
  |                    ----------- borrow later used here
  |
help: consider using a `let` binding to create a longer lived value
  |
2 ~     let binding = std::env::var("APP_ENV");
3 ~     let environment = binding.as_deref().unwrap_or("development");
  |

For more information about this error, try `rustc --explain E0716`.

进一步使用变量遮蔽来防止创建另一个标识符,你可以使用:
let environment = env::var("APP_ENV");
let environment = environment.as_deref().unwrap_or("development");

注意:对于未包装的拥有字符串,您可以通过使用引用运算符和范围运算符来避免额外的声明,而不是使用as_deref(),因为引用运算符可以防止编译器提前丢弃拥有的值。不幸的是,似乎没有对于包装的ResultOption类型的等效技巧。
let foo = &String::from("foo")[..];

参见:
- [Idiomatic way to set string default value to unwrap](link1) - [String slice vs as_ref](link2) - [Why is it legal to borrow a temporary?](link3)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接