Разница между &*conn
и conn.borrow()
заключается в том, что тип может иметь несколько значений заимствования .
use std::borrow::Borrow;
fn main() {
let input = vec![1, 2, 3];
let _slice: &[u8] = input.borrow(); // ok
let _vec_ref: &Vec<u8> = input.borrow(); // also ok
let _slice: &[u8] = &*input; // ok
// let _vec_ref: &Vec<u8> = &*input; // error!
}
Выражение &*conn
использует черту Deref
, где каждый тип может иметь только одну реализацию Deref
. Однако тип может иметь несколько реализаций Borrow<X>
для разных X
s.
Когда вы пишете
read_exact(conn.borrow(), [0u8]);
Компилятору необходимо решить следующие обязательства:
Rc<TcpStream>: Borrow<X1>
из-за использования borrow()
&X1: AsyncRead
из-за read_exact
Обратите внимание, что X1
- неизвестный тип. Компилятору необходимо выяснить все потенциальные X1
и посмотреть, сможет ли кто-либо выполнить оба обязательства. Обязательство 2 каким-то образом оценивается в первую очередь, что заканчивается следующими кандидатами:
impl<X2> AsyncRead for &PollEvented<X2> where &X2: Read
impl AsyncRead for &TcpStream
impl AsyncRead for &[u8]
и, возможно, более неважные кандидаты ...
Опять же, каким-то образом кандидат 1 выбирается перед кандидатом 2. Это приводит к следующему новому набору обязательств после выбора кандидата 1:
Rc<TcpStream>: Borrow<PollEvented<X2>>
&PollEvented<X2>: AsyncRead
решено!
&X2: Read
, что приводит к выбору impl<X3> Read for &PollEvented<X3> where &X3: Read
, и с этого момента решатель застрял в бесконечном цикле и в конце концов сдался.
Подробности о том, как компилятор решает эти уравнения, можно найти в Rust Compiler Guide .
Хорошей новостью является то, что система черт обновляется для использования стандартных методов логического вывода (таких как Prolog), которые позволяют правильно выводить программу OP.
Однако, до того, как будет реализован новый движок черт, если вы должны использовать borrow
, вы могли бы немного помочь компилятору, сказав ему, что X1
должно быть:
read_exact::<&TcpStream, _>(conn.borrow(), [u8]);
// ^~~~~~~~~~ forces &X1 = &TcpStream
Если вам интересно, следующая программа chalk доказывает, что новый решатель может проверить пример OP
trait Borrow<T> {}
trait AsyncRead {}
trait Read {}
struct Ref<T> {} // meaning &T
struct Rc<T> {}
impl<T> Borrow<T> for Rc<T> {}
struct TcpStream {}
impl Read for TcpStream {}
impl AsyncRead for TcpStream {}
impl Read for Ref<TcpStream> {}
impl AsyncRead for Ref<TcpStream> {}
struct PollEvented<E> {}
impl<E> AsyncRead for Ref<PollEvented<E>> where Ref<E>: Read {}
impl<E> Read for Ref<PollEvented<E>> where Ref<E>: Read {}
// Verify:
//
// ?- exists<X> { Ref<X>: AsyncRead, Rc<TcpStream>: Borrow<X> }
// Unique; substitution [?0 := TcpStream], lifetime constraints []