2016-07-15 46 views
8

Ich habe einige nicht-kopierbaren Typ und eine Funktion, und (vielleicht) verbraucht, produziert sie:Wie kann ich eine Box wiederverwenden, aus der ich den Wert verschoben habe?

type Foo = Vec<u8>; 

fn quux(_: Foo) -> Option<Foo> { 
    Some(Vec::new()) 
} 

nun eine Art betrachten, die sehr ähnlich Box irgendwie konzeptionell ist:

struct NotBox<T> { 
    contents: T 
} 

können wir schreiben eine Funktion, die in etwas Inhalt des NotBox auszieht und legt vorübergehend zurück, bevor es zurückkehrt:

fn bar(mut notbox: NotBox<Foo>) -> Option<NotBox<Foo>> { 
    let foo = notbox.contents; // now `notbox` is "empty" 
    match quux(foo) { 
     Some(new_foo) => { 
      notbox.contents = new_foo; // we put something back in 
      Some(notbox) 
     } 
     None => None 
    } 
} 

Ich mag eine analoge Funktion schreiben, die mit Box es funktioniert, aber die Compiler es nicht mögen:

fn baz(mut abox: Box<Foo>) -> Option<Box<Foo>> { 
    let foo = *abox; // now `abox` is "empty" 
    match quux(foo) { 
     Some(new_foo) => { 
      *abox = new_foo; // error: use of moved value: `abox` 
      Some(abox) 
     } 
     None => None 
    } 
} 

ich Some(Box::new(new_foo)) statt zurückkehren konnte, aber das führt unnötige Zuweisung - ich bereits etwas Speicher zur Verfügung habe! Ist es möglich, das zu vermeiden?

Ich würde auch die Beseitigung der match Aussagen zu bekommen, aber wieder der Compiler ist nicht glücklich mit ihm (auch für die NotBox Version):

fn bar(mut notbox: NotBox<Foo>) -> Option<NotBox<Foo>> { 
    let foo = notbox.contents; 
    quux(foo).map(|new_foo| { 
     notbox.contents = new_foo; // error: capture of partially moved value: `notbox` 
     notbox 
    }) 
} 

Ist es möglich, um daran zu arbeiten?

+3

Sie scheinen mehr als o fragen ne Frage hier. Die Übereinstimmung vs. Karte sieht so aus, als ob sie zu einer neuen Frage bewegt werden sollte. –

+0

Ich wollte den ersten Teil beantworten, realisierte aber, dass ich noch nicht verstehe, wie der Wechsel von 'Box ' funktioniert; Es scheint nicht mit "Deref" oder "DerefMut" Eigenschaften verwandt zu sein. Freue mich also auf eine gute Antwort! –

+0

@ChrisEmerson Der Match-Teil war etwas, das auftauchte, als ich versuchte, das minimale Beispiel meines Problems zu erstellen, also habe ich nicht viel geforscht. Ich dachte nur, dass dies wahrscheinlich mit der allgemeinen Frage und der Tatsache zusammenhängt, dass ich nicht verstehe, wie "partielle" Bewegungen funktionieren, also habe ich es hier gelassen. – mrhania

Antwort

8

Also, aus einem Box herausziehen ist ein besonderer Fall ... was nun?

Das Modul std::mem bietet eine Reihe sicherer Funktionen zum Verschieben von Werten, ohne Löcher (!) In die Speichersicherheit von Rust zu bohren.Von Interesse sind hier swap und replace:

fn baz(mut abox: Box<Foo>) -> Option<Box<Foo>> { 
    let foo = std::mem::replace(&mut *abox, Foo::default()); 

    match quux(foo) { 
     Some(new_foo) => { 
      *abox = new_foo; 
      Some(abox) 
     } 
     None => None 
    } 
} 

Es hilft auch bei der map Fall, weil es nicht leihen sich die Box:

pub fn replace<T>(dest: &mut T, src: T) -> T 

Was wir wie so verwenden können

fn baz(mut abox: Box<Foo>) -> Option<Box<Foo>> { 
    let foo = std::mem::replace(&mut *abox, Foo::default()); 

    quux(foo).map(|new_foo| { *abox = new_foo; abox }) 
} 
+2

Der Nachteil ist, dass 'Foo:. default()' muss existieren und hoffentlich billig auszuführen Wenn weder wahr ist, dann. Standardeinstellung schaltet der Vorschlag auf eine 'Box >' wäre nützlich, wie dann können Sie 'take' anrufen und' None' die billig zu erstellen ist – Shepmaster

+0

@Shepmaster. Vereinbarte :) jedoch eher auf den Stick würde ich Szenario vorgestellt und nur ausgearbeitet, wenn das OP konkretere Bedenken hat, sonst fürchte ich, die Antwort könnte jeden ertränken um es zu lesen. –

4

Das Verschieben von Boxen wird im Compiler speziell behandelt. Du kannst etwas aus ihnen herausbewegen, aber du kannst etwas nicht zurückbewegen, weil der Akt des Auszugs auch die Zuordnung aufhebt. Sie können etwas albernes mit std::ptr::write, , und std::ptr::replace tun, aber es ist schwer, es richtig zu machen, weil etwas gültig innerhalb eines Box sein sollte, wenn es fallengelassen wird. Ich würde empfehlen, nur die Zuweisung zu akzeptieren, oder stattdessen zu einem Box<Option<Foo>> wechseln.

+1

Mein größtes Ärgernis mit Rust ist, dass einige Details wie diese offenbar nicht außerhalb von Diskussionsfäden oder manchmal im Vorbeigehen in RFCs dokumentiert sind. :-(FWIW, ist es erwähnt [in diesem verschoben RFC] (https://github.com/rust-lang/rfcs/pull/178). –

+2

@ChrisEmerson i in diesem Fall denken, ist das spezielle Gehäuse von 'Box' etwas, das dem vorgeschlagenen 'DerefMove' Zug entfernt werden und verwandelt will. Ich will nicht zu viel schmutzige Wäsche zeigen, und haben es Widley internalisiert werden, wenn es mehr wiederverwendbar und Generika werden kann. Beachten sie, dass die [RFC neu gestartet wurde] (https://github.com/rust-lang/rfcs/pull/1646) als auch – Shepmaster

0

Wir können eine Funktion schreiben, die vorübergehend Inhalt der NotBox auszieht und legt etwas zurück, bevor es zurückkehrt

Das ist, weil Sie teilweise von der Struktur bewegen können, dass Sie mit dem Wert annehmen. Es verhält sich so, als ob alle Felder separate Variablen wären. Das ist jedoch nicht möglich, wenn die Struktur implementiert Drop, weil drop die gesamte Struktur benötigt, immer gültig (im Falle von Panik).

Zur Problemumgehung haben Sie nicht genügend Informationen bereitgestellt - insbesondere warum bazBox als Argument und warum quux nicht akzeptiert werden kann? Welche Funktionen gehören Ihnen und welche sind Teil einer API, die Sie nicht ändern können? Was ist der wahre Typ von Foo? Ist es gross?

Die beste Problemumgehung wäre, Box überhaupt nicht zu verwenden.