gpt4 book ai didi

rust - 为什么 by_ref().take() 的用法在 Iterator 和 Read 特征之间不同?

转载 作者:行者123 更新时间:2023-11-29 07:44:25 25 4
gpt4 key购买 nike

这里有两个函数:

fn foo<I>(iter: &mut I)
where
I: std::iter::Iterator<Item = u8>,
{
let x = iter.by_ref();
let y = x.take(2);
}

fn bar<I>(iter: &mut I)
where
I: std::io::Read,
{
let x = iter.by_ref();
let y = x.take(2);
}

虽然第一个编译正常,但第二个给出了编译错误:

error[E0507]: cannot move out of borrowed content
--> src/lib.rs:14:13
|
14 | let y = x.take(2);
| ^ cannot move out of borrowed content

by_reftake 的签名在 std::iter::Iteratorstd::io 中几乎相同: :Read traits,所以我假设如果第一个编译,第二个也会编译。我哪里错了?

最佳答案

这确实是一条令人困惑的错误消息,而且您收到它的原因相当微妙。 answer by ozkriff正确解释这是因为 Read trait 不在范围内。我想添加更多上下文,并解释为什么您会收到您看到的特定错误,而不是未找到该方法的错误。

take() Read 上的方法和 Iterator需要 self按值(value),或者换句话说,它消耗它的接收者。这意味着您只有拥有接收者的所有权才能调用它。您问题中的函数接受 iter通过可变引用,所以他们不拥有底层 I对象,所以你不能调用 <Iterator>::take()<Read>::take()对于底层对象。

但是,正如 ozkriff 所指出的,标准库提供了 Iterator 的“转发”实现。和 Read对实现各自特征的类型的可变引用。当您调用 iter.take(2)在你的第一个函数中,你实际上最终调用了 <&mut Iterator<Item = T>>::take(iter, 2) ,它只消耗你对迭代器的可变引用,而不是迭代器本身。这是完全正确的;虽然函数不能使用迭代器本身,因为它不拥有迭代器,但函数确实拥有引用。但是,在第二个函数中,您最终调用了 <Read>::take(*iter, 2)。 ,它试图消耗底层读者。由于您不拥有该阅读器,您会收到一条错误消息,说明您无法将其移出借用的上下文。

那么为什么第二个方法调用解析为不同的方法呢? ozkriff 的回答已经解释了发生这种情况是因为 Iterator特征在标准序曲中,而Read默认情况下,特征不在范围内。让我们更详细地看一下方法查找。它记录在 "Method call expressions" 部分中Rust 语言引用:

The first step is to build a list of candidate receiver types. Obtain these by repeatedly dereferencing the receiver expression's type, adding each type encountered to the list, then finally attempting an unsized coercion at the end, and adding the result type if that is successful. Then, for each candidate T, add &T and &mut T to the list immediately after T.

根据这个规则,我们的候选类型列表是

&mut I, &&mut I, &mut &mut I, I, &I, &mut I

Then, for each candidate type T, search for a visible method with a receiver of that type in the following places:

  1. T's inherent methods (methods implemented directly on T).

  2. Any of the methods provided by a visible trait implemented by T. If T is a type parameter, methods provided by trait bounds on T are looked up first. Then all remaining methods in scope are looked up.

案例I: Iterator , 这个过程从查找 take() 开始&mut I 上的方法. &mut I 上没有固有方法, 自 I是泛型类型,所以我们可以跳过步骤 1。在步骤 2 中,我们首先查找关于 &mut I 的特征边界的方法。 , 但只有 I 的特征边界, 所以我们继续查找 take()在范围内的所有剩余方法上。自 Iterator在范围内,我们确实从标准库中找到了转发实现,并且可以停止处理我们的候选类型列表。

对于第二种情况,I: Read , 我们也从 &mut I 开始, 但自 Read不在范围内,我们不会看到转发实现。一旦我们到达 I不过,在我们的候选类型列表中,关于特征边界提供的方法的条款开始出现:首先查找它们,无论特征是否在范围内。 I具有 Read 的特征界限, 所以 <Read>::take()被发现。正如我们在上面看到的,调用此方法会导致错误消息。

总而言之,特征必须在范围内才能使用它们的方法,但即使特征不在范围内,也可以使用特征边界上的方法。

关于rust - 为什么 by_ref().take() 的用法在 Iterator 和 Read 特征之间不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52515361/

25 4 0
文章推荐: syntax - Rust 中的 <- 符号是什么?
文章推荐: java - ArrayList 发送槽套接字