Tại sao việc thêm một hàm thứ hai ngăn chặn sự ép buộc của deref đối số?


10

Tôi đã gặp vấn đề này khi cố gắng thêm impl Add<char> for Stringvào thư viện chuẩn. Nhưng chúng ta có thể sao chép nó một cách dễ dàng, mà không cần các nhà điều hành shenanigans. Chúng tôi bắt đầu với điều này:

trait MyAdd<Rhs> {
    fn add(self, rhs: Rhs) -> Self;
}

impl MyAdd<&str> for String {
    fn add(mut self, rhs: &str) -> Self {
        self.push_str(rhs);
        self
    }
}

Đủ đơn giản. Với điều này, đoạn mã sau sẽ biên dịch:

let a = String::from("a");
let b = String::from("b");
MyAdd::add(a, &b);

Lưu ý rằng trong trường hợp này, biểu thức đối số thứ hai ( &b) có kiểu &String. Sau đó, nó được kết hợp lại &strvà chức năng gọi hoạt động.

Tuy nhiên , chúng ta hãy thử thêm hàm ý sau:

impl MyAdd<char> for String {
    fn add(mut self, rhs: char) -> Self {
        self.push(rhs);
        self
    }
}

( Mọi thứ trên Sân chơi )

Bây giờ MyAdd::add(a, &b)biểu thức trên dẫn đến lỗi sau:

error[E0277]: the trait bound `std::string::String: MyAdd<&std::string::String>` is not satisfied
  --> src/main.rs:24:5
   |
2  |     fn add(self, rhs: Rhs) -> Self;
   |     ------------------------------- required by `MyAdd::add`
...
24 |     MyAdd::add(a, &b);
   |     ^^^^^^^^^^ the trait `MyAdd<&std::string::String>` is not implemented for `std::string::String`
   |
   = help: the following implementations were found:
             <std::string::String as MyAdd<&str>>
             <std::string::String as MyAdd<char>>

Tại sao vậy? Đối với tôi có vẻ như việc cưỡng chế deref chỉ được thực hiện khi chỉ có một ứng cử viên chức năng. Nhưng điều này có vẻ sai với tôi. Tại sao các quy tắc sẽ được như vậy? Tôi đã thử xem qua các đặc điểm kỹ thuật, nhưng tôi không tìm thấy bất cứ điều gì về sự ép buộc đối số.


Điều này nhắc nhở tôi về câu trả lời này (tôi đã viết). Trình biên dịch biết đặc điểm chung và khi chỉ có một implđặc điểm áp dụng, nó có thể định hướng bằng cách chọn đối số loại được sử dụng trong đó impl. Trong phần hỏi đáp khác, tôi đã sử dụng khả năng này để khiến trình biên dịch (dường như) chọn một impltại trang web cuộc gọi, đây là điều mà nó thường không thể làm được. Có lẽ trong này trường hợp đó là những gì cho phép nó để làm deref cưỡng chế. Nhưng đó chỉ là dự đoán.
trentcl

2
Dưới đây là một nhận xét nói rằng nếu chỉ tìm thấy một hàm giả thì trình biên dịch "háo hức xác nhận" nó, cho phép các sự ép buộc deref (trong số những thứ khác) xảy ra. Điều đó không xảy ra đối với nhiều ứng viên impl. Vì vậy, tôi đoán đó là loại câu trả lời, nhưng tôi vẫn muốn biết thêm. Chương này trong cuốn sách Rustc có thể giúp ích, nhưng theo như tôi có thể nói, nó không nói cụ thể điều gì về điều này.
Lukas Kalbertodt

Câu trả lời:


0

Như bạn đã giải thích, trình biên dịch xử lý trường hợp chỉ có một giá trị implđặc biệt hợp lệ và có thể sử dụng điều này để điều khiển suy luận kiểu:

Dưới đây là một nhận xét nói rằng nếu chỉ tìm thấy một hàm giả thì trình biên dịch "háo hức xác nhận" nó, cho phép các sự ép buộc deref (trong số những thứ khác) xảy ra. Điều đó không xảy ra đối với nhiều ứng viên impl.

Phần thứ hai là sự ép buộc deref sẽ chỉ xảy ra tại các địa điểm mà loại dự kiến ​​được biết đến, nó không xảy ra theo suy đoán. Xem các trang web cưỡng chế trong tài liệu tham khảo. Lựa chọn Impl và suy luận kiểu trước tiên phải tìm thấy rõ ràng điều đó MyAdd::add(&str)sẽ được mong đợi, để cố gắng ép buộc đối số &str.

Nếu một workaround là cần thiết trong tình huống này, sử dụng một biểu thức như &*bhoặc &b[..]hoặc b.as_str()cho đối số thứ hai.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.