Разрешены ли полиморфные переменные?

У меня есть разные структуры, которые реализуют один и тот же признак. Я хочу разветвиться на каком-то условии, решив во время выполнения, какую из этих структур создать. Затем, независимо от того, в какой ветке я следил, я хочу вызвать методы из этой черты.

Возможно ли это в Rust? Я надеюсь получить что-то вроде следующего (что не компилируется):

trait Barks { fn bark(&self); } struct Dog; impl Barks for Dog { fn bark(&self) { println!("Yip."); } } struct Wolf; impl Barks for Wolf { fn bark(&self) { println!("WOOF!"); } } fn main() { let animal: Barks; if 1 == 2 { animal = Dog; } else { animal = Wolf; } animal.bark(); } 

Да, но не так легко. То, что вы там написали, заключается в том, что animal должно быть переменной типа Barks , но Barks – это черта; описание интерфейса. Черты не имеют статически заданного размера, так как может возникнуть тип любого размера и impl Barks . Компилятор понятия не имеет, насколько велики animal .

Что вам нужно сделать, это добавить слой косвенности. В этом случае вы можете использовать Box , хотя вы также можете использовать такие вещи, как Rc или простые ссылки:

 fn main() { let animal: Box; if 1 == 2 { animal = Box::new(Dog); } else { animal = Box::new(Wolf); } animal.bark(); } 

Здесь я помещаю Dog или Wolf в кучу, а затем бросаю это в Box . Это похоже на то, что вы добавляете объект к интерфейсу в нечто вроде C # или Java или бросаете Dog* в Barks* на C ++.

Совсем другой подход, который вы могли бы использовать, – это enums. Вы могли бы enum Animal { Dog, Wolf } затем определить impl Animal { fn bark(&self) { ... } } . Зависит от того, нужен ли вам совершенно открытый набор животных и / или несколько признаков.

Наконец, обратите внимание, что «вид» выше. Существуют различные вещи, которые не работают, как в Java / C # / C ++. Например, у Rust нет downcasting (вы не можете перейти из Box обратно в Box или от одного признака к другому). Кроме того, это работает только в том случае, если признак является «безопасным для объекта» (никаких дженериков, без использования значения self или Self ).

У DK есть хорошее объяснение, я просто перезвоню с примером, где мы выделяем Dog или Wolf в стеке, избегая выделения кучи:

 fn main() { let dog; let wolf; let animal: &Barks; if 1 == 2 { dog = Dog; animal = &dog; } else { wolf = Wolf; animal = &wolf; } animal.bark(); } 

Это немного уродливо, но ссылки выполняют ту же самую косвенность, что и Box с меньшим количеством накладных расходов.

Определение пользовательского enums – наиболее эффективный способ сделать это. Это позволит вам выделять в стеке ровно столько пространства, которое вам нужно, то есть размер самой большой опции, плюс 1 дополнительный байт, чтобы отслеживать, какая опция хранится. Он также обеспечивает прямой доступ без уровня косвенности, в отличие от решений, использующих ссылку Box или ссылку на свойство.

К сожалению, для этого требуется больше котельной плиты:

 enum WolfOrDog { IsDog(Dog), IsWolf(Wolf) } use WolfOrDog::*; impl Barks for WolfOrDog { fn bark(&self) { match *self { IsDog(ref d) => d.bark(), IsWolf(ref w) => w.bark() } } } fn main() { let animal: WolfOrDog; if 1 == 2 { animal = IsDog(Dog); } else { animal = IsWolf(Wolf); } animal.bark(); } 

В main мы используем только одну выделенную стек, которая содержит экземпляр нашего пользовательского enums.

  • Как преобразовать 'struct' в '& '?
  • В чем разница между iter и in_iter?
  • Почему Rust не поддерживает повышение уровня объекта?
  • Что такое monoморфизация с контекстом на C ++?
  • Мутируемое «я» при чтении с объекта владельца
  • Автоматически реализовать черты замкнутого типа для новых типов Rust (кортежи с одним полем)
  • Зачем пытаться! () И? не компилироваться при использовании в функции, которая не возвращает результат?
  • Как увидеть расширенный макрокод, вызывающий ошибку компиляции?
  • Почему Iterator :: take_while получает право собственности на iterator?
  • Как вы можете сделать безопасный статический синглтон в Rust?
  • Есть ли способ создать псевдоним типа для нескольких признаков?
  • Interesting Posts

    Удаление дубликатов файлов с помощью командного файла Windows

    Внешние жесткие диски, поврежденные сектора, восстанавливают / маркируют утилиту в Linux

    c # возвращающая ошибка “не все пути кода возвращают значение”

    Что может привести к сложности алгоритма O (log n)?

    Как вы получаете встроенный инструмент сравнения Eclipse, чтобы игнорировать различия в пробелах?

    Не удалось обновить версию Windows 10 от Home до Pro

    Сжатие текстуры Android OpenGL

    JQuery: создайте HTML в «памяти», а не DOM

    Изменение размера раздела Windows 7, работающего на VirtualBox с динамически распределенным хранилищем

    Как отключить содержимое HTML5 в популярных браузерах, таких как Firefox и Chrome?

    Как изменить цвет ячеек на основе сегодняшней даты в Excel 2010

    Как клонировать загрузочный раздел Win7 на жесткий жесткий диск?

    Настройка автоматической автоподстройки фокуса и автоматической задержки автозапуска в Windows 7

    duplicate ‘row.names’ не допускается ошибка

    Отслеживание использования приложений в течение дня

    Давайте будем гением компьютера.