Давайте еще раз посмотрим на ваш последний пример (сокращенный мной):
trait GenericAssociated {
type GenericAssociated;
}
impl<G> GenericAssociated for Struct {
type GenericAssociated = G;
}
Это не имеет общие родственные типы!У вас просто есть универсальный тип в вашем блоке impl
, который вы назначаете для связанного типа.Мм, хорошо, я вижу, откуда возникла путаница.
Ваш пример ошибок с «параметром типа G
не ограничен признаком impl, self-типом или предикатами».Это не изменится, когда GAT будут внедрены, потому что, опять же, это не имеет ничего общего с GAT.
Использование GAT в вашем примере может выглядеть так:
trait Associated {
type Associated<T>; // <-- note the `<T>`! The type itself is
// generic over another type!
// Here we can use our GAT with different concrete types
fn user_choosen<X>(&self, v: X) -> Self::Associated<X>;
fn fixed(&self, b: bool) -> Self::Associated<bool>;
}
impl Associated for Struct {
// When assigning a type, we can use that generic parameter `T`. So in fact,
// we are only assigning a type constructor.
type Associated<T> = Option<T>;
fn user_choosen<X>(&self, v: X) -> Self::Associated<X> {
Some(x)
}
fn fixed(&self, b: bool) -> Self::Associated<bool> {
Some(b)
}
}
fn main() {
Struct.user_choosen(1); // results in `Option<i32>`
Struct.user_choosen("a"); // results in `Option<&str>`
Struct.fixed(true); // results in `Option<bool>`
Struct.fixed(1); // error
}
Но, чтобы ответить на ваш главный вопрос:
В чем разница между чертойуниверсальный тип и универсальный связанный тип?
Вкратце: они позволяют задержать применение конкретного типа (или срока службы), что делает всю систему типов более мощной.
Существует множество мотивационных примеров в RFC , в частности, потоковый итератор и пример семейства указателей.Давайте быстро разберемся, почему потоковый итератор не может быть реализован с помощью обобщенных признаков.
GAT-версия потокового итератора выглядит следующим образом:
trait Iterator {
type Item<'a>;
fn next(&self) -> Option<Self::Item<'_>>;
}
В текущем Rust мы могли бы поместитьПараметр времени жизни на признаке вместо связанного с ним типа:
trait Iterator<'a> {
type Item;
fn next(&'a self) -> Option<Self::Item>;
}
Пока все хорошо: все итераторы могут реализовать эту черту, как и раньше.Но что, если мы хотим использовать это?
fn count<I: Iterator<'???>>(it: I) -> usize {
let mut count = 0;
while let Some(_) = it.next() {
count += 1;
}
count
}
Какое время жизни мы должны аннотировать?Помимо аннотирования времени жизни 'static
, у нас есть два варианта:
fn count<'a, I: Iterator<'a>>(it: I)
: это не сработает, потому что вызывающий объект выбирает универсальные типы функций.Но it
(который станет self
в вызове next
) живет в нашем стековом фрейме.Это означает, что время жизни it
не известно вызывающей стороне.Таким образом мы получаем компилятор ( Playground ).Это не вариант. fn count<I: for<'a> Iterator<'a>>(it: I)
(с использованием HRTB): похоже, это работает, но у него есть тонкие проблемы.Теперь нам требуется I
для реализации Iterator
для любого времени жизни 'a
.Это не проблема для многих итераторов, но некоторые итераторы возвращают элементы, которые не существуют вечно, и поэтому они не могут реализовать Iterator
для любого времени жизни - просто времена жизни, которые короче, чем их элемент.Использование этих границ с более высоким рейтингом часто приводит к секретным 'static
границам, которые очень ограничивают.Так что это также не всегда работает.
Как вы можете видеть: мы не можем правильно записать границы I
.И на самом деле, мы даже не хотим упоминать время жизни в сигнатуре count
!Это не должно быть необходимо.И это именно то, что нам позволяют делать GAT (между прочим).С GAT мы могли бы написать:
fn count<I: Iterator>(it: I) { ... }
И это будет работать.Поскольку «применение конкретного времени жизни» происходит только тогда, когда мы вызываем next
.
. Если вас интересует еще больше информации, вы можете взглянуть на мое сообщение в блоге «Решение обобщенной потоковой передачи».Проблема итератора без GAT », где я пытаюсь использовать универсальные типы для признаков, чтобы обойти отсутствие GAT.И (спойлер): обычно это не работает.