Когда целесообразно использовать связанный тип по сравнению с общим типом?
на этот вопрос, возникла проблема, которая может быть решена путем изменения попытки использования параметра универсального типа В связанный тип. Это вызвало вопрос "почему связанный тип более уместен здесь?- что заставило меня захотеть узнать больше.
The RFC, который ввел связанные типы говорит:
в этом документе уточняется соответствие признака владельца:
- обработка всех параметров типа признака как типы входов и
- предоставление связанных типов, которые вывод:.
RFC использует структуру графика в качестве мотивирующего примера, и это также используется в документация, но я признаю, что не полностью оценил преимущества связанной версии типа по сравнению с параметризованной версией типа. Главное, что distance метод не должен заботиться о Edge тип. Это хорошо, но кажется немного мелкой причиной для того, чтобы иметь связанные типы вообще.
я обнаружил, что связанные типы довольно интуитивно понятны для использования на практике, но я затрудняюсь решить, где и когда я должен использовать их в своем собственном API.
при написании кода, когда я должен выбрать соответствующий тип параметра универсального типа, и когда я должен сделать наоборот?
3 ответа:
теперь это описано в второе издание Язык Программирования Ржавчины. Однако давайте немного погрузимся в дополнение.
давайте начнем с простого примера.
когда целесообразно использовать метод черта?
есть несколько способов, чтобы обеспечить позднее связывание:
trait MyTrait { fn hello_word(&self) -> String; }или:
struct MyTrait<T> { t: T, hello_world: fn(&T) -> String, } impl<T> MyTrait<T> { fn new(t: T, hello_world: fn(&T) -> String) -> MyTrait<T>; fn hello_world(&self) -> String { (self.hello_world)(self.t) } }игнорируя любые реализация / стратегия производительности, оба отрывка выше позволяют пользователю указать в динамическом порядке, как
hello_worldдолжны вести себя.единственное различие (семантически) заключается в том, что
traitреализация гарантирует, что для данного типаTреализацияtrait,hello_worldвсегда будет иметь такое же поведение, тогда какstructреализация позволяет иметь различное поведение на основе экземпляра.подходит ли использование метода или нет зависит от usecase!
когда целесообразно использовать связанный тип?
аналогично
traitметоды выше, связанный тип является формой позднего связывания (хотя это происходит при компиляции), что позволяет пользователюtraitуказать для данного экземпляра, какой тип для замены. Это не единственный способ (Таким образом, вопрос):trait MyTrait { type Return; fn hello_world(&self) -> Self::Return; }или:
trait MyTrait<Return> { fn hello_world(&Self) -> Return; }эквивалентны позднему связыванию методов выше:
- первый обеспечивает это для данного
Selfесть одинReturnсвязан- второй, вместо этого, позволяет реализовать
MyTraitнаSelfза несколькоReturnкакая форма является более подходящей, зависит от того, имеет ли смысл применять единство или нет. Например:
Derefиспользует связанный тип, потому что без единства компилятор сошел бы с ума во время выводAddиспользует связанный тип, потому что его автор думал, что с учетом двух аргументов будет логический тип возвратаКак видите, пока
Derefявляется очевидным usecase (техническое ограничение), случайAddменее ясно: может быть, это имело бы смысл дляi32 + i32выход либоi32илиComplex<i32>в зависимости от контекста? Тем не менее автор осуществил свое решение и постановил, что перегрузка возвращения тип для дополнений был излишним.моя личная позиция заключается в том, что нет правильного ответа. Тем не менее, помимо аргумента unicity, я бы упомянул, что связанные типы облегчают использование признака, поскольку они уменьшают количество параметров, которые должны быть указаны, поэтому в случае, если преимущества гибкости использования обычного параметра признака не очевидны, я предлагаю начать с связанного типа.
связанные типы-это группировка механизм, поэтому они должны использоваться, когда имеет смысл группировать типы вместе.
The
Graphчерта, представленная в документации, является примером этого. Вы хотитеGraphчтобы быть общим, но как только у вас есть определенный видGraph, вы не хотитеNodeилиEdgeтипы не различаются. А конкретноGraphне собирается менять эти типы в рамках одной реализации, и на самом деле, хочет, чтобы они всегда быть одним и тем же. Они сгруппированы вместе, или можно даже сказать связан.