2017-05-21 4 views
1

Wenn ich diesen Code:Was bewirkt die Wo-Klausel in einem Merkmal?

trait Trait { 
    fn f(&self) -> i32 where Self: Sized; 

    fn g(&self) -> i32; 
} 


fn object_safety_dynamic(x: &Trait) { 
    x.f(); // error 
    x.g(); // works 
} 

Was bedeutet die where Klausel tatsächlich tun?

Naiv, dachte ich where Self: Sized; etwas über die Art diktiert Trait Implementierung, wie ‚wenn Sie Trait für Typ implementieren AA Ihre Art bemessen sein müssen, das heißt, es kann aber nicht i32[i32] sein.

Eine solche Einschränkung würde jedoch eher gehen als trait Trait: Sized (korrigieren Sie mich, wenn ich falsch liege)?

Jetzt habe ich festgestellt, where Self: Sized; tatsächlich bestimmt, ob ich f oder g innerhalb object_safety_dynamic aufrufen kann.

Meine Fragen:

  1. Was hier passiert hinter den Kulissen?

  2. Was (in einfachen Englisch) bin ich eigentlich sagen den Compiler von where Self: Sized;, die g() Arbeit macht, aber f() nicht?

  3. Insbesondere: Da &self sowieso eine Referenz ist, besteht der kompilierte Unterschied zwischen f und g für verschiedene (Größe oder Größe) Typen. Wäre es nicht immer zu etwas wie _vtable_f_or_g(*self) -> i32, unabhängig von where oder wenn der Typ ist oder nicht?

  4. Warum kann ich Trait für beide u8 und [u8] implementieren. Sollte mich der Compiler nicht davon abhalten, f() für [u8] zu implementieren, anstatt einen Fehler an der Call-Site zu werfen?

+0

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. –

+0

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

+0

@PeterHall, Ja, danke! – left4bread

Antwort

3

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.

3

fn f(&self) -> i32 where Self: Sized;

Diese besagt, dass f nur für Typen definiert ist, die auch Sized implementieren. Unsized-Typen können weiterhin Trait implementieren, aber f ist nicht verfügbar.

Inside object_safety_dynamic, Aufruf x.f() ist tatsächlich dabei: (*x).f(). Während x ist Größe, weil es ein Zeiger ist, *x möglicherweise nicht, weil es beliebig Implementierung Trait sein könnte. Aber Code innerhalb der Funktion muss für jedes gültige Argument funktionieren, so dass Sie dort x.f() nicht aufrufen dürfen.

Verwandte Themen