Was macht die Where-Klausel?
Naiv dachte ich, wo Self: Sized; diktiert etwas über den Typ, der Eigenschaft implementiert, wie 'Wenn Sie Eigenschaft für Typ A implementieren, muss Ihr Typ A eine Größe haben, d. h. es kann i32 sein, aber nicht [i32].
Eine solche Einschränkung würde eher als Merkmal Trait gehen: Sized
Das ist richtig.
In diesem Fall gilt die Grenze jedoch nur für die Funktion. where
Grenzen auf Funktionen werden nur auf der Callsite überprüft.
Was passiert hier hinter den Kulissen?
Es gibt eine verwirrende wenig über Syntax Rust, die ist, dass zu beiden Trait
- das Merkmal
Trait
beziehen kann; oder
- Das "Merkmal Objekt"
Trait
, die eigentlich ein Typ ist, kein Objekt.
Sized
ist ein Merkmal, und jede Art T
die Sized
ist, kann seine Größe als Konstante genommen haben, die von std::mem::size_of::<T>()
. Solche Typen, die keine Größe haben, sind str
und [u8]
, deren Inhalt keine feste Größe hat.
Der Typ Trait
ist ebenfalls unsized. Intuitiv ist dies, weil Trait
als ein Typ aus allen Werten von Typen besteht, die das Merkmal Trait
implementieren, die unterschiedliche Größe haben können. Dies bedeutet, dass Sie nie einen Wert vom Typ Trait
haben können - Sie können nur einen über einen "fetten Zeiger" wie &Trait
oder Box<Trait>
usw. beziehen. Diese haben die Größe von 2 Zeigern - einen für eine V-Tabelle, einen für die Daten. Es sieht in etwa wie folgt aus:
struct &Trait {
pub data: *mut(),
pub vtable: *mut(),
}
Es automatisch ein impl der Form ist:
impl Trait /* the trait */ for Trait /* the type */ {
fn f(&self) -> i32 where Self: Sized { .. }
fn g(&self) -> i32 {
/* vtable magic: something like (self.vtable.g)(self.data) */
}
}
Was (in einfachem Englisch) bin ich sagen, tatsächlich den Compiler von dem Selbst: Sized; das macht g() funktionieren aber f() nicht?
Beachten Sie, dass da, wie bereits erwähnt, Trait
ist nicht Sized
, die gebundenen Self: Sized
nicht erfüllt und so die Funktion f
nicht, wo Self == Trait
genannt werden.
Insbesondere: Da & Selbst eine Referenz ist, gibt es einen kompilierten Unterschied zwischen f und g für verschiedene (Größe oder Größe) Typen. Wäre es nicht immer so etwas wie _vtable_f_or_g (* self) -> i32, egal wo oder ob der Typ ist oder nicht?
Der Typ Trait
ist immer unsized. Es spielt keine Rolle, welcher Typ zu Trait
gezwungen wurde.Die Art und Weise Sie die Funktion mit einem Sized
Variable nennen, ist es direkt zu verwenden:
fn generic<T: Trait + Sized>(x: &T) { // the `Sized` bound is implicit, added here for clarity
x.f(); // compiles just fine
x.g();
}
Warum kann ich Trait für beide U8 implementieren und [U8]. Sollte mich der Compiler nicht davon abhalten, f() für [u8] zu implementieren, anstatt einen Fehler an der Call-Site zu verursachen?
Da die Eigenschaft nicht durch Self: Sized
begrenzt ist - die Funktion f
ist. Es gibt also nichts, was Sie davon abhält, die Funktion zu implementieren - es ist nur so, dass die Grenzen der Funktion niemals erfüllt werden können, so dass Sie sie niemals aufrufen können.
Ich denke, du hast 'f' und' g' falsch herum. 'x.g()' sollte funktionieren, während 'x.f()' ein Fehler sein wird. Das liegt daran, dass "f" nur für Typen definiert ist, die "Größe" sind, aber es ist nicht garantiert, dass "x" "Größe" ist, weil nicht alle "Impls" von "Eigenschaft" sind. –
Durch Festlegen der Einschränkung für das Merkmal wird festgelegt, welche Typen das Merkmal implementieren können. Durch die Einschränkung der Funktion innerhalb des Merkmals wird festgelegt, welche Implementierungstypen die Funktion aufrufen können. – Alec
@PeterHall, Ja, danke! – left4bread