2016-10-07 2 views
3

Ich möchte eine generische Funktion deklarieren, die Merkmalsobjekte akzeptiert und nur Merkmalsobjekte. Ich möchte das, weil ich diese löschen möchte und sie als TraitObject Objekte über eine ABI-Grenze übergeben.Kann ich eine generische Typbindung haben, die erfordert, dass dieser Typ ein Merkmal ist?

Eine Funktion wie folgt geschrieben wird nicht kompilieren ...

fn f<T: ?Sized>(t: &T) -> std::raw::TraitObject { 
    unsafe { std::mem::transmute(t) } 
} 

... mit dem folgenden Fehler:

error[E0512]: transmute called with differently sized types: &T (pointer to T) to std::raw::TraitObject (128 bits) 

Ich verstehe, warum der Compiler in verschiedenen Größen klagt: &T können sei ein Zeiger auf einen konkreten Typ (wie &i32), der ein einzelner Zeiger (64 Bits) oder ein Merkmalsobjekt (wie &Display) ist, das zwei Zeiger mit demselben Layout wie std::raw::TraitObject (128 Bits) sein wird.

Diese Funktion sollte in Ordnung sein, solange &T ein Merkmalsobjekt ist, d. H. T ist ein Merkmal. Gibt es eine Möglichkeit, diese Anforderung auszudrücken?

+0

Und um klar zu sein, kann es kein * spezifisches * Merkmalsobjekt sein? Ursache 'fn f (t: & SomeTrait)' wäre immer ein Merkmalsobjekt. – Shepmaster

+0

@Shempmaster Korrekt. Ohne die Generika ist alles schön. –

Antwort

3

Wenn Sie use transmute_copy instead haben, können Sie den Compiler die Größenabweichungen ignorieren lassen. Dies bedeutet jedoch, dass Sie solche Probleme selbst behandeln müssen, z. Überprüfen Sie die Größe selbst und vielleicht in Panik, wenn es ein Missverhältnis ist. Andernfalls kann dies zu einem nicht definierten Verhalten führen.

fn f<T: ?Sized>(t: &T) -> std::raw::TraitObject { 
    assert!(std::mem::size_of::<&T>() == std::mem::size_of::<std::raw::TraitObject>()); 
    unsafe { std::mem::transmute_copy(&r) } 
} 
3

Es ist unmöglich, eine negative zu beweisen ... aber soweit ich weiß, die Antwort ist nein, sorry.

Die Darstellung von TraitObject ist instabil, insbesondere weil Rust in der Zukunft möglicherweise mehrere virtuelle Zeiger auf einen einzelnen Datenzeiger (der zum Beispiel &(Display + Eq) darstellt) anheften kann.

In der Zwischenzeit verwende ich Low-Level-Speicher-Tricks, um den virtuellen Zeiger und Datenzeiger zu lesen und dann selbst TraitObject zu bauen; durch einen Anruf an mem::size_of geschützt, um sicherzustellen, dass &T hat die richtige Größe für 2 *mut(), weil ?Sized bedeutet Sized oder nicht (und nicht !Sized).

+0

Ich habe gerade festgestellt, dass die Verwendung von 'transmute_copy' anstelle von' transmute' ausreicht, um die Funktion kompilieren zu lassen, und es scheint zu funktionieren (solange T ein Merkmal ist) https: //play.rust- lang.org/?gist=072c6e41e3887b81062c5e2672baefc3&version=nightly&backtrace=0. Ist das die Art von Low-Level-Memory-Tricks, an die Sie gedacht haben? –

+0

(Auf einer Randnotiz: JESUS ​​Ich habe 100k + rep und ich kann nicht einen verkürzten Spielplatz Link in einem Kommentar aus irgendeinem gottlosen Grund posten) –

+0

@ R.MartinhoFernandes: Nein war es nicht; es ist eigentlich viel besser. Ich bin überrascht, dass es mit unsedierten Typen funktioniert, da es in der Dokumentation keine "Größe" gibt (https://doc.rust-lang.org/std/mem/fn.transmute_copy.html). Ich ermutige Sie, dies als Antwort zu posten, es ist viel besser als herumspielen mit Zeigern.Ich ermutige Sie auch, eine Größenprüfung zu verwenden, um zu überprüfen, dass die übergebenen Daten zur Laufzeit * wirklich * 128 Bit (und nicht 64) sind, da 'transmute_copy' nicht prüft (und bei Bedarf ein undefiniertes Verhalten hat) Lesen über die Grenzen des Quellobjekts hinaus). –

Verwandte Themen