Как работает механизм, лежащий в основе создания коробочных признаков?
Мне трудно понять, как возникают ценности упакованных черт. Рассмотрим следующий код:
trait Fooer {
fn foo(&self);
}
impl Fooer for i32 {
fn foo(&self) { println!("Fooer on i32!"); }
}
fn main() {
let a = Box::new(32); // works, creates a Box<i32>
let b = Box::<i32>::new(32); // works, creates a Box<i32>
let c = Box::<Fooer>::new(32); // doesn't work
let d: Box<Fooer> = Box::new(32); // works, creates a Box<Fooer>
let e: Box<Fooer> = Box::<i32>::new(32); // works, creates a Box<Fooer>
}
Очевидно, что варианты А и в работают тривиально. Однако вариант с этого не делает, вероятно, потому, что функция new
принимает только значения одного и того же типа, что не имеет места, так как Fooer != i32
. Варианты d и e работают, что позволяет мне подозревать, что выполняется какое-то автоматическое преобразование из Box<i32>
в Box<Fooer>
.
Итак, мои вопросы таковы:
- делает здесь происходит какое-то преобразование? Если да, то что за механизм стоит за этим и как он работает? (Меня также интересуют детали низкого уровня, то есть то, как материал представлен под капотом) Есть ли способ создать
Box<Fooer>
непосредственно из i32
? Если нет, то почему?
2 ответа:
Однако вариант с этого не делает, вероятно, потому, что функция
new
принимает только значения одного и того же типа, что не имеет места, так какFooer != i32
.Нет, это потому, что не существует никакой
new
функции дляBox<dyn Fooer>
. В документации :
impl<T> Box<T>
pub fn new(x: T) -> Box<T>
Большинство методов на
Фактически, этот метод не может существовать. Для того, чтобы упаковать что-то, вам нужно знать его размер. Чтобы передать его в функцию, необходимо знать его размер. Чтобы даже переменная содержала что-то, она должна иметь размер. Безразмерные типы, такие какBox<T>
позволяютT: ?Sized
, ноnew
определяется вimpl
без AT: ?Sized
связанного. Это означает, что вы можете вызватьBox::<T>::new
только тогда, когдаT
является тип с известным размером.dyn Fooer
не имеет размера, поэтому методnew
просто не может быть вызван.dyn Fooer
, могут существовать только за" жирным указателем", то есть указателем на объект и указателем на объект. реализацияFooer
для этого объекта.Как вы получаете жирный указатель? Вы начинаете с тонкого указателя ипринуждаете его. Вот что происходит в этих двух строках:
let d: Box<Fooer> = Box::new(32); // works, creates a Box<Fooer> let e: Box<Fooer> = Box::<i32>::new(32); // works, creates a Box<Fooer>
Box::new
возвращает aBox<i32>
, который затем принуждается кBox<Fooer>
. Вы можете считать это преобразованием, ноBox
не изменяется; все, что делает компилятор, - это вставляет дополнительный указатель на него и забывает его исходный тип. ответ Родриго более подробно описывает механика этого принуждения на уровне языка.Надеюсь, все это объясняет, почему ответ на
Есть ли способ создатьBox<Fooer>
непосредственно изi32
?- это "нет":
i32
должен быть упакован , прежде чем можно будет стереть его тип. Это та же самая причина, по которой вы не можете писатьlet x: Fooer = 10i32
.Связанные
Я попытаюсь объяснить, какие преобразования (coertions) происходят в вашем коде.
Существует маркерная черта, названная
Unsize
это, между другими:Unsize реализован для:
T
- этоUnsize<Trait>
, КогдаT: Trait
.- [...]
Эта черта, насколько мне известно, не используется непосредственно для coertions. Вместо этого
CoerceUnsized
используется. Эта черта реализуется во множестве случаев, некоторые из них вполне ожидаемы, такие как как:impl<'a, 'b, T, U> CoerceUnsized<&'a U> for &'b T where 'b: 'a, T: Unsize<U> + ?Sized, U: ?Sized
, который используется для принуждения
&i32
к&Fooer
.Интересная, не столь очевидная реализация этой черты, которая влияет на ваш код:
impl<T, U> CoerceUnsized<Box<U>> for Box<T> where T: Unsize<U> + ?Sized, U: ?Sized
Это, вместе с определением маркера
Unsize
, может быть в некоторой степени прочитано как: ЕслиU
является признаком иT
реализуетU
, тоBox<T>
можно принудительно ввести вBox<U>
.По поводу вашего последнего вопроса:
Насколько мне известно, нет. Проблема заключается в том, чтоЕсть ли способ создать коробку непосредственно из i32? Если нет, то почему - нет?
Box::new(T)
требует размерного значения, так как передаваемое значение перемещается в поле, а некрупные значения не могут быть перемещены. На мой взгляд, самый простой способ сделать это-просто написать:let c = Box::new(42) as Box<Fooer>;
То есть вы создаете
Box
нужного типа и затем принуждаете к неназначенному (обратите внимание, что он выглядит очень похожим на ваш примерd
).