#rust
Вопрос:
Из Colorize
документа о чертах, String
не включает его (в то время как amp;str включает его).
fn blue(self) -> ColoredString
where
Self: Sized,
Но почему String
тип может вызывать свой метод?
use colored::Colorize;
fn main() {
let blue = "blue".to_owned().blue();
println!("{}", blue);
}
правки
- [неразрешено] Как я могу его удалить? (в частности, чтобы выяснить, происходит ли автоматическое отключение)
- Что происходит под капотом?
Введите принуждение при вызовах методов. String
может быть принужден к amp;str
amp;*String
Ответ №1:
Признак Colorize
реализован для amp;'a str
и String
реализует Deref<Target=str>
.
Правила вызова метода Rust означают, что значение может быть автоматически заимствовано или разыменовано.
Это означает , что для нашего вызова метода String
Rust будет искать какой-либо метод, который имеет один из следующих типов в качестве типа приемника
String
amp;String
amp;mut String
str
(автор дереф)amp;str
(это позволяет вызову метода работать)amp;mut str
Поскольку String
он только заимствуется, вы можете использовать его после звонка .blue()
use colored::Colorize;
fn main() {
let x = "hello".to_string();
let y = x.blue();
println!("{}", x);
println!("{}", y);
}
Комментарии:
1. Я думаю
x
, что перемещается, как его называют, сblue
тех пор, как он получаетself
. Правильно, это тип принуждения! Трудно запомнить эти кусочки сахара.2.
x
не перемещается, такself
как естьamp;str
, а общие ссылки естьCopy
.
Ответ №2:
Причина String
, по-видимому, в поддержке черт, реализованных для amp;str
, связана с его реализацией Deref
с целевым типом str
. Сама строка на самом деле не поддерживает эти функции, но .
оператор действует как своего рода разыменование, предоставляющее доступ к методам, поддерживаемым amp;str
.
Ниже приведен минимальный объем кода, необходимый для изоляции и демонстрации того же поведения.
#[derive(Debug)]
enum ColoredString { Blue }
trait Colorize {
fn blue(self) -> ColoredString
where
Self: Sized,
{
ColoredString::Blue
}
}
impl Colorize for amp;str { } // <-- `Colorize` only impl'd for `amp;str`.
fn main() {
let s = "a string".to_string();
println!("{:?}", s.blue()); // <-- Invoking `.blue()` on `String`
}
выход
Blue
Реализуя Colorize
for amp;str
, .blue()
можно вызвать on String
. Если мы реализуем Deref
для произвольной структуры то же самое String
, мы должны быть в состоянии воспроизвести то же поведение.
: // Continued from above example...
:
impl Colorize for amp;str { } // <-- `Colorize` only impl'd for `amp;str`.
use std::ops::Deref;
struct Foo { } // <-- Arbitrary struct.
impl Deref for Foo { // <-- Implements `Deref<Target = str>`.
type Target = str;
fn deref(amp;self) -> amp;Self::Target {
"I am Foo!"
}
}
fn main() {
let f = Foo { };
println!("{:?}", amp;*f); // <-- Simple deref.
println!("{:?}", f.blue()); // <-- Invoking `.blue()` on `Foo`.
}
выход
"I am Foo!"
Blue
И это работает; Foo
вышеуказанные поддержки Colorize
точно так String
же работают, реализуя Deref
с Target = str
помощью .
Foo
Colorize
на самом деле эта черта вообще не поддерживается — она просто возвращает amp;str
значение при разыменовании, которое компилятор учитывает при разрешении вызова метода.
Комментарии:
1. но , читая источник
colored
, это только подразумеваетamp;str
.2. в этом случае его трудно расширить из-за правила сироты; У меня нет ни цветного, ни строкового источника. Оказывается, это связано с принуждением типа при вызовах методов.
3. @Izana ах, я понимаю.. Сейчас я смотрю на источники и вижу , что в них нет явного
impl Colored for String
, но каким-то образом компилятор справляется с вызовом признака в строке.4. Хорошо @Izana… У меня есть отдельный пример того, как происходит волшебство. И в моем примере я получил произвольную структуру для поддержки
Colorize
, используя тот же механизм, который обеспечиваетString
поддержку той же черты.