rust - `impl Trait` 类型的返回值如何借用检查?
问题描述
以下代码无法编译:
fn foo<'a, F: Fn() -> &'a str>(vec: Vec<i32>, fun: F) -> impl Iterator<Item = i32> {
println!("{}", fun());
vec.into_iter()
}
fn main() {
let s = "hello, world!".to_string();
let iter = foo(vec![1, 2, 3], || &s);
drop(s);
for x in iter {
println!("{}", x);
}
}
error[E0505]: cannot move out of `s` because it is borrowed
--> src/main.rs:9:10
|
8 | let iter = foo(vec![1, 2, 3], || &s);
| -- - borrow occurs due to use in closure
| |
| borrow of `s` occurs here
9 | drop(s);
| ^ move out of `s` occurs here
10 |
11 | for x in iter {
| ---- borrow later used here
如果我将foo
签名替换为
fn foo<'a, F: Fn() -> &'a str>(vec: Vec<i32>, fun: F) -> <Vec<i32> as IntoIterator>::IntoIter {
// ...
}
这让我相信impl Trait
类型的借用检查更加保守:编译器假定返回的对象捕获fun
,即使它没有捕获。
然而,这个有趣的例子编译得很好:
fn foo(s: &str) -> impl Iterator<Item = i32> {
println!("{}", s);
vec![1, 2, 3].into_iter()
}
fn main() {
let s = "hello, world!".to_string();
let iter = foo(&s);
drop(s);
for x in iter {
println!("{}", x);
}
}
这里编译器似乎不假设返回的impl Iterator<Item = i32>
借用s
。
返回impl Trait
的类型究竟是如何借用检查的?什么时候假设他们借用了其他函数参数,比如第一种情况?他们什么时候假设不这样做,就像在后一种情况下一样?
解决方案
我相信这个问题评论在这里讲述了这个故事。这听起来像是对保守的类型系统的故意限制,但我同意问题作者的观点,即能够选择退出是件好事:
这种行为的核心原因是 impl Trait 在返回位置的变化行为:返回的 impl Trait 始终是所有通用输入参数的变体,即使在技术上没有使用。这样做是为了如果您更改返回的公共 API 的内部实现,
impl Trait
您不必担心向 API 引入额外的方差参数。这可能会破坏下游代码,因此对于 Rust 的 semver 系统来说是不可取的。
推荐阅读
- javascript - 儿童的 jQuery 选择器
- java - 使用@Retryable 打印重试次数
- c# - 如何为位置记录定义额外的初始化逻辑?
- python - 从数据库调用数据而不使用 for 循环
- python - 不使用 OpenCV 的阈值
- flutter - Flutter better_player 在 Google PlayStore 中不起作用
- azure - 通过 Microsoft Graph 和 App Credentials 添加来宾邀请给 Method Not Found
- google-cloud-firestore - 在数据存储模式下使用 UUID 作为 Google Cloud Firestore 中的键是否安全?
- javascript - 从数组中抓取对象后使用对象的功能
- python - 泰勒级数展开以导出 cos 角值