Когда целесообразно использовать связанный тип по сравнению с общим типом?
на этот вопрос, возникла проблема, которая может быть решена путем изменения попытки использования параметра универсального типа В связанный тип. Это вызвало вопрос "почему связанный тип более уместен здесь?- что заставило меня захотеть узнать больше.
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
не собирается менять эти типы в рамках одной реализации, и на самом деле, хочет, чтобы они всегда быть одним и тем же. Они сгруппированы вместе, или можно даже сказать связан.