Проблема
Характеристика (trait
), которая является обобщенной по типу контейнера, имеет требования к спецификации типов - пользователи характеристики должны указать все ее обобщенные типы.
В следующем примере характеристика Contains
позволяет использовать обобщенные типы A
и B
. Затем характеристика реализуется для типа Container
, при этом для A
и B
указывается i32
, чтобы можно было использовать ее с fn difference()
.
Поскольку Contains
является обобщенной, мы вынуждены явно указать все обобщенные типы для fn difference()
. На практике мы хотим иметь способ выражать, что A
и B
определяются входным значением C
. Как вы увидите в следующем разделе, ассоциированные типы предоставляют именно такую возможность.
struct Container(i32, i32);
// Характеристика, которая проверяет, хранятся ли 2 элемента внутри контейнера.
// Также извлекает первое или последнее значение.
trait Contains<A, B> {
fn contains(&self, _: &A, _: &B) -> bool; // Явно требует `A` и `B`.
fn first(&self) -> i32; // Не явно требует `A` или `B`.
fn last(&self) -> i32; // Не явно требует `A` или `B`.
}
impl Contains<i32, i32> for Container {
// Возвращает true, если хранящиеся числа равны.
fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
(&self.0 == number_1) && (&self.1 == number_2)
}
// Возвращает первое число.
fn first(&self) -> i32 { self.0 }
// Возвращает последнее число.
fn last(&self) -> i32 { self.1 }
}
// `C` содержит `A` и `B`. Соответственно, повторно указывать `A` и
// `B` является неудобством.
fn difference<A, B, C>(container: &C) -> i32 where
C: Contains<A, B> {
container.last() - container.first()
}
fn main() {
let number_1 = 3;
let number_2 = 10;
let container = Container(number_1, number_2);
println!("Содержит ли контейнер {} и {}: {}",
&number_1, &number_2,
container.contains(&number_1, &number_2));
println!("Первое число: {}", container.first());
println!("Последнее число: {}", container.last());
println!("Разница равна: {}", difference(&container));
}