Каковы различия между несколькими способами создания структур нулевого размера? - PullRequest
0 голосов
/ 03 мая 2018

Я нашел четыре разных способа создания struct без данных:

  • struct A{} // empty struct / empty braced struct
    
  • struct B(); // empty tuple struct
    
  • struct C(()); // unit-valued tuple struct
    
  • struct D; // unit struct
    

(я оставляю произвольно вложенные кортежи, которые содержат только () s и одно-вариантные enum объявления вне вопроса, так как я понимаю, почему они не должны использоваться).

В чем различия между этими четырьмя декларациями? Буду ли я использовать их для определенных целей или они взаимозаменяемы?

Книга и справочник оказались на удивление бесполезными. Я нашел этот принятый RFC ( clarified_adt_kinds ) , который немного учитывает различия, а именно, что структура модуля также объявляет постоянное значение D и что структуры кортежа также объявляют конструкторы B() и C(_: ()). Тем не менее, он не предлагает рекомендации по проектированию, почему использовать какой.

Полагаю, что при экспорте их с pub существуют различия в том, какие виды могут быть созданы вне моего модуля, но я не нашел убедительной документации по этому поводу.

Ответы [ 2 ]

0 голосов
/ 04 мая 2018

Между этими четырьмя определениями есть только два функциональных различия (и пятую возможность, о которой я упомяну через минуту):

  1. Синтаксис (самый очевидный). ответ Маккартона более подробно.
  2. Когда структура помечена pub, может ли ее конструктор (также называемый struct буквальный синтаксис ) использоваться вне модуля, в котором она определена.

Единственный из ваших примеров, который не может быть непосредственно создан извне текущего модуля, это C. Если вы попытаетесь это сделать, вы получите ошибку:

mod stuff {
    pub struct C(());
}
let _c = stuff::C(());  // error[E0603]: tuple struct `C` is private

Это происходит потому, что поле не помечено pub; если вы объявите C как pub struct C(pub ()), ошибка исчезнет.

Есть еще одна возможность, о которой вы не упомянули, которая дает чуть более описательное сообщение об ошибке: обычная структура с нулевым размером не pub элемента.

mod stuff {
    pub struct E {
        _dummy: (),
    }
}
let _e = stuff::E { _dummy: () };  // error[E0451]: field `_dummy` of struct `main::stuff::E` is private

(Опять же, вы можете сделать поле _dummy доступным вне модуля, объявив его с помощью pub.)

Поскольку конструктор E можно использовать только внутри модуля stuff, stuff имеет исключительный контроль над тем, когда и как создаются значения E. Многие структуры в стандартной библиотеке используют это преимущество, например Box (для наглядного примера). Типы с нулевым размером работают точно так же; на самом деле, извне модуля, в котором он определен, единственный способ узнать, что непрозрачный тип имеет нулевой размер, это вызвать mem::size_of.

0 голосов
/ 04 мая 2018
struct D; // unit struct

Это обычный способ написать нулевой размер struct.

struct A{} // empty struct / empty braced struct
struct B(); // empty tuple struct

Это просто особые случаи базовых struct и кортежей struct, которые не имеют параметров. RFC 1506 объясняет рациональное разрешение для тех (они не привыкли):

Разрешить структуры кортежей и варианты кортежей с 0 полями. Это ограничение является искусственным и может быть снято тривиально. Авторы макросов, имеющие дело со структурами / вариантами кортежей, будут рады избавиться от этого особого случая.

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

struct C(()); // unit-valued tuple struct

Это еще один особый случай кортежа struct. В Rust () - это тип, как и любой другой тип, поэтому struct C(()); не сильно отличается от struct E(u32);. Хотя сам тип не очень полезен, запрет на него может привести к еще одному особому случаю, который должен быть обработан в макросах или обобщенных типах (например, struct F<T>(T) можно создать как F<()>).

Обратите внимание, что есть много других способов иметь пустые типы в Rust. Например. можно получить функцию, возвращающую Result<(), !>, чтобы указать, что она не выдает значение и не может завершиться ошибкой. Хотя вы можете подумать, что возвращение () в этом случае было бы лучше, вы могли бы иметь , чтобы сделать это, если вы реализуете черту, которая требует от вас возвращать Result<T, E>, но позволяет вам выбрать T = () и E = !.

...