Ответ 1
EDIT обновлен до Rust 1.5
Отказ от ответственности: это только результаты нескольких экспериментов, которые я сделал, в сочетании с чтением блога Нико Мацакиса.
DST - это типы, размер которых не обязательно известен во время компиляции.
До DST
Несостоявшийся вектор, такой как [i32]
или голый признак, такой как IntoIterator
, не был допустимым типом объектов, поскольку они не имеют известного размера.
До DST структура может выглядеть так:
struct Foo { f: [i32; 2] } // [i32; 2] is a fixed-sized vector with 2 i32 elements
struct Foo2<'a> { f: &'a [i32] } // & is basically a pointer.
// The compiler always knows the size of a
// pointer on a specific architecture, so whatever
// size the [i32] has, its address (the pointer) is
// a statically-sized type too
но не так:
struct Foo { f: [i32] } // f is (statically) unsized, so Foo is unsized too
Это верно и для перечислений и кортежей.
С DST
Вы можете объявить struct (/enum/tuple), например, Foo выше, содержащий нестандартный тип. Как было сказано, тип, содержащий нестандартный тип, также будет несинтетическим.
Обратите внимание, что при определении Foo
было легко, создание экземпляра Foo все еще очень тяжело и может быть изменено. Поскольку вы не можете технически создать нестандартный тип по определению, вам нужно будет создать размерный экземпляр Foo
(например, Foo { f: [1, 2, 3] }
, который является Foo<[i32; 3]>
так со статически известным размером) и закодируйте какую-нибудь сантехнику, чтобы позволить rustc знать, как это может принудить это к его статически несертифицированному аналогу Foo<[i32]>
. Способ сделать это в безопасной и стабильной Rust по-прежнему обрабатывается с Rust 1.5 (здесь приведена RFC для принуждения DST для получения дополнительной информации),
К счастью, определение нового DST не является чем-то, что вы, вероятно, будете делать, если только вы не создаете новый тип интеллектуального указателя (например, Rc), который должен быть достаточно редким явлением.
Теперь представьте себе, что Rc определяется как наш Foo выше. Поскольку у него есть все сантехника, чтобы сделать принуждение от размера до нестандартного, его можно использовать для этого:
use std::rc::Rc;
trait Foo { fn foo(&self) { println!("foo") } }
struct Bar;
impl Foo for Bar {}
fn main() {
let data: Rc<Foo> = Rc::new(Bar);
// we're creating a statically typed version of Bar
// and coercing it (the :Rc<Foo> on the left-end side)
// to as unsized bare trait counterpart.
// Rc<Foo> is a trait object, so it has no statically
// known size
data.foo();
}
? Размерная граница
Итак, так как вы вряд ли создадите новый DST, какие DST полезны для повседневной кодировки Rust? Чаще всего вы можете писать общий код, который работает как по размерным типам, так и по их существующим нестандартным аналогам. Чаще всего это будут Vec/[] slices или String/str.
То, как вы выражаете это, связано с ?Sized
"bound" (в кавычках, потому что ?Sized
в некотором роде противоположно привязке, на самом деле говорит, что T может быть как размерами, так и нестандартными, поэтому он расширяет возможные типы, которые мы могут использовать вместо того, чтобы ограничивать их, как правило, имеют ограничения).
Продуманное примерное время! Скажем, что у нас есть структура FooSized
, которая просто обертывает ссылку и простую черту Print
, которую мы хотим реализовать для нее.
struct FooSized<'a, T>(&'a T) where T:'a;
trait Print {
fn print(&self);
}
Теперь мы хотим определить оболочку impl для всех обернутых T, которые реализуют Display.
impl<'a, T> Print for FooSized<'a, T> where T:'a + fmt::Display {
fn print(&self) { println!("{}", self.0) }
}
Теперь попробуйте сделать так:
// Does not compile. "hello" is a &'static str, so self print is str
// (which is not sized)
let h_s = FooSized("hello");
h_s.print();
// to make it work we need a &&str or a &String
let s = "hello"; // &'static str
let h_s = &s; // & &str
h_s.print(); // now self is a &str
Эх... это неудобно... К счастью, у нас есть способ обобщить структуру, чтобы работать непосредственно с str
(и вообще несертифицированными типами): ?Sized
//same as before, only added the ?Sized bound
struct Foo<'a, T: ?Sized>(&'a T) where T:'a;
impl<'a, T: ?Sized> Print for Foo<'a, T> where T:'a + fmt::Display {
fn print(&self) { println!("{}", self.0) }
}
теперь это работает:
let h = Foo("hello");
h.print();
Для менее надуманного (но простого) фактического примера вы можете посмотреть на Borrow в стандартной библиотеке.
Вернуться к вашему вопросу
trait Foo for ?Sized {
fn foo(&self) -> i32;
}
Синтаксис for ?Sized
теперь устарел. Он имел обыкновение ссылаться на тип Self
(другими словами, он заявил, что Foo может быть реализован нестандартным типом), но теперь это значение по умолчанию. Любая черта теперь может быть реализована для нестандартного типа, т.е. Теперь вы можете:
trait Foo {
fn foo(&self) -> i32;
}
impl Foo for [i32] { //[i32] is unsized, but rustc does not complain for this impl
fn foo(&self) -> i32 { 5 }
}
Если вы не хотите, чтобы ваша черта была реализована для нестандартных типов, вы можете использовать Sized bound:
trait Foo : Sized { // now the impl Foo for [i32] is illegal
fn foo(&self) -> i32;
}