Универсальный синтаксис вызова функций (universal function call syntax)
Иногда, функции могут иметь одинаковые имена. Рассмотрим этот код:
trait Foo {
fn f(&self);
}
trait Bar {
fn f(&self);
}
struct Baz;
impl Foo for Baz {
fn f(&self) { println!("Baz’s impl of Foo"); }
}
impl Bar for Baz {
fn f(&self) { println!("Baz’s impl of Bar"); }
}
let b = Baz;
Если мы попытаемся вызвать b.f()
, то получим ошибку:
error: multiple applicable methods in scope [E0034]
b.f();
^~~
note: candidate #1 is defined in an impl of the trait `main::Foo` for the type
`main::Baz`
fn f(&self) { println!("Baz’s impl of Foo"); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: candidate #2 is defined in an impl of the trait `main::Bar` for the type
`main::Baz`
fn f(&self) { println!("Baz’s impl of Bar"); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Нам нужен способ указать, какой конкретно метод нужен, чтобы устранить неоднозначность. Эта возможность называется «универсальный синтаксис вызова функций», и выглядит это так:
# trait Foo {
# fn f(&self);
# }
# trait Bar {
# fn f(&self);
# }
# struct Baz;
# impl Foo for Baz {
# fn f(&self) { println!("Baz’s impl of Foo"); }
# }
# impl Bar for Baz {
# fn f(&self) { println!("Baz’s impl of Bar"); }
# }
# let b = Baz;
Foo::f(&b);
Bar::f(&b);
Давайте разберемся.
Foo::
Bar::
Эти части вызова задают один из двух видов типажей: Foo
и Bar
. Это то, что
на самом деле устраняет неоднозначность между двумя методами: Rust вызывает
метод того типажа, имя которого вы используете.
f(&b)
Когда мы вызываем метод, используя синтаксис вызова метода, как
например b.f()
, Rust автоматически заимствует b
, если f()
принимает в
качестве аргумента &self
. В этом же случае, Rust не будет использовать
автоматическое заимствование, и поэтому мы должны явно передать &b
.
Форма с угловыми скобками
Форма UFCS, о которой мы только что говорили:
Trait::method(args);
Это сокращенная форма записи. Ниже представлена расширенная форма записи, которая требуется в некоторых ситуациях:
<Type as Trait>::method(args);
Синтаксис <>::
является средством предоставления подсказки типа. Тип
располагается внутри <>
. В этом случае типом является Type as Trait
,
указывающий, что мы хотим здесь вызвать Trait
версию метода. Часть as Trait
является необязательной, если вызов не является неоднозначным. То же самое что с
угловыми скобками, отсюда и короткая форма.
Вот пример использования длинной формы записи.
trait Foo {
fn clone(&self);
}
#[derive(Clone)]
struct Bar;
impl Foo for Bar {
fn clone(&self) {
println!("Making a clone of Bar");
<Bar as Clone>::clone(self);
}
}
Этот код вызывает метод clone()
типажа Clone
, а не типажа Foo
.