Сообщение об ошибке несколько вводит в заблуждение. Num
- это признак, и он является типом динамического размера, поэтому вы не можете иметь его значения без какой-либо косвенности (ссылка или Box
). Причина этого проста; просто задайте себе вопрос: какой размер (в байтах) expr
значения перечисления должны иметь? Это, конечно, не менее String
, но как насчет Num
? Произвольные типы могут реализовать эту черту, поэтому для того, чтобы быть здоровым, expr
должен иметь бесконечный размер!
Следовательно, вы можете использовать черты как типы только с каким-либо указателем: &Num
или Box<Num>
. Указатели всегда имеют фиксированный размер, а объекты признаков - «жирные» указатели, сохраняя в них дополнительную информацию, чтобы помочь при диспетчеризации метода.
Также признаки обычно используются в качестве границ для параметров типового типа. Поскольку generics monomorphized, они превращаются в статические типы в скомпилированном коде, поэтому их размер всегда статически известен, и им не нужны указатели. Использование дженериков должно быть по умолчанию, и вы должны переключиться на объекты объектов только тогда, когда знаете, почему дженерики не будут работать для вас.
Это возможные варианты определения вашего типа. С дженериков:
enum Expr<N: Num> {
Numeric(N),
Symbol(String)
}
Тре объекта через ссылку:
enum Expr<'a> { // '
Numeric(&'a Num + 'a),
Symbol(String)
}
Черта объекта с коробкой:
enum Expr {
Numeric(Box<Num + 'static>), // ' // I used 'static because numbers usually don't contain references inside them
Symbol(String)
}
Вы можете прочитать больше о дженериков и черт в the official guide, хотя в момент не содержит информации о объектах признаков. Пожалуйста, спросите, не понимаете ли вы что-то.
Обновление
'a
в
enum Expr<'a> { // '
Numeric(&'a Num + 'a),
Symbol(String)
}
является параметром времени жизни. Он определяет как время жизни ссылки, так и внутренних объектов объекта внутри Numeric
. &'a Num + 'a
- это тип, который вы можете прочитать как «объект-признак за ссылкой, который живет как минимум до 'a
со ссылками внутри него, которые также живут как минимум до 'a
». То есть, во-первых, вы указываете 'a
в качестве эталонного ресурса: &'a
, и, во-вторых, вы определяете срок службы внутренних объектов объектов: Num + 'a
. Последнее необходимо, потому что черты могут быть реализованы для любых типов, включая те, которые содержат ссылки внутри них, поэтому вам нужно также установить минимальное время жизни этих ссылок в тип объекта-объекта, в противном случае проверка перевода не будет корректно работать с объектами признаков.
С Box
ситуация очень похожа. Box<Num + 'static>
- это «объект-признак внутри выделенного кучи окна со ссылками внутри него, которые живут как минимум до тех пор, пока 'static
». Тип Box
- это интеллектуальный указатель на принадлежащие кучу данные. Поскольку он владеет данными, которые он хранит, ему не нужен параметр времени жизни, например, ссылки. Однако объект-признак все еще может содержать ссылки внутри него, поэтому Num + 'a
по-прежнему используется; Я просто решил использовать время жизни 'static
вместо добавления другого параметра времени жизни. Это связано с тем, что числовые типы обычно просты и не содержат ссылок внутри них, и это эквивалентно 'static
. Конечно, вы можете добавить параметр продолжительности жизни, если хотите.
Обратите внимание, что все эти варианты являются правильными:
&'a SomeTrait + 'a
&'a SomeTrait + 'static
Box<SomeTrait + 'a> // '
Box<SomeTrait + 'static>
Даже это правильно, с 'a
и 'b
как различных параметров жизни:
&'a SomeTrait + 'b
хотя это редко используется, потому что 'b
должен быть как минимум до 'a
(в противном случае внутренности объекта-объекта могут быть недействительными, пока он сам по-прежнему жив), поэтому вы можете просто использовать &'a SomeTrait + 'a
.
Общая версия неудобна для использования, поскольку создание символа 'Symbol' может привести к тому, что компилятор не сможет вывести конкретный тип для' N'. Более того, если в конечном итоге 'Expr' эволюционирует, чтобы содержать варианты, содержащие подвыражения, общая версия не будет масштабироваться. Я согласен с тем, что в общем случае общую версию следует рассматривать первыми, но в этой ситуации я считаю, что это неправильное решение. Кроме того, в зависимости от требований может быть достаточно заменить «Numeric (N)» двумя вариантами: «Integer (i64)» и «Float (f64)», чтобы избежать бокса. –
Спасибо, что это было действительно полезно. У меня есть вопрос, хотя добавление 'a в числовом (& 'a Num +' a) и добавление' static в Numeric (Box) делает , –
ragingSloth
@ragingSloth, я обновил свой ответ. –