2016-02-03 3 views
7

Also ich stapfte ich zur Zeit auf eine struct Initialisierung durch eine Yaml Datei in Heap-Speicher laden:Wie man Struktur mit Heap-Referenzen idiomatisch erzeugt?

extern crate yaml_rust; 

use std::io::prelude::*; 
use std::fs; 
use yaml_rust::{Yaml, YamlLoader}; 

struct Petrovich { 
    middlename: Yaml, 
    firstname: Yaml, 
    lastname: Yaml 
} 

impl Petrovich { 

    fn new() -> Petrovich { 

     // Open Rules File (Panics on error) 
     let mut f = fs::File::open("./src/rules.yml").unwrap(); 
     // Create String Buffer and Read to it 
     let mut buffer = String::new(); 
     f.read_to_string(&mut buffer).unwrap(); 
     // Pass Buffer to Yaml and unwrap 
     let rules: &Yaml = &YamlLoader::load_from_str(&buffer).unwrap()[0]; 

     // Return Petrovich with preloaded rules 
     Petrovich { 
      middlename: rules["middlename"], 
      firstname: rules["firstname"], 
      lastname: rules["lastname"] 
     } 
    } 
} 

aber ich ein cannot move out of indexed content Fehler. Wie gehe ich damit um?

EDIT: Ich bin mit yaml-rust für dieses und haben eine Datei rules.yml mit firstname, lastname und middlename Felder enthalten innerhalb ./src

Quelle ist hier zu finden: https://github.com/Latrasis/petrovich-rs

Antwort

2

Yaml ist Clone, so können Sie .clone() auf der Referenz rufen Sie einfach an seinen Inhalt „out“ kopieren:

Petrovich { 
     middlename: rules["middlename"].clone(), 
     firstname: rules["firstname"].clone(), 
     lastname: rules["lastname"].clone(), 
    } 

Ich ziehe dies auf die akzeptierte Lösung, da die Daten kopiert werden, ist klein und es beruht nicht auf der Implementierung von yaml::Hash. Das Klonen eines ganzen Dokumentenbaums könnte eine andere Sache sein.

Möglicherweise haben Sie hier ein Argument für eine Funktion, die yaml-rost hinzugefügt werden soll. Das Verschieben eines Elements aus einer Struktur scheint eine sinnvolle Anforderung zu sein, die API unterstützt sie jedoch nicht direkt.

+0

Danke! Das ist eigentlich viel besser lesbar! – latrasis

3

bewegen Sie versuchen, aus einem geliehener Zeiger (weil rules[str] eine &Yaml zurückgibt), aber das ist nicht legal. Wir müssen Funktionen verwenden, die es uns ermöglichen, einen Wert aus dem Hauptobjekt zu verschieben. Dies ändert das Hauptobjekt , aber das ist hier in Ordnung, da wir es am Ende der Funktion verwerfen werden.

Zunächst müssen wir verstehen, was die Indizierung mit einem String tut. The implementation gibt nur dann einen signifikanten Wert zurück, wenn der Wert ein Hashwert ist, und ruft den zugrunde liegenden Wert ab, indem ein Wert Yaml::String zum Indizieren des Hashwerts erstellt wird.

Die Yaml::Hash Variante enthält eine BTreeMap<Yaml, Yaml>. Die Bibliothek bietet die as_hash Convenience-Methode, um darauf zuzugreifen, aber dies gibt nur einen unveränderlichen Zeiger. Wir müssen den Mustervergleich verwenden, um einen veränderbaren Zeiger zu erhalten.

Als nächstes werden wir die remove method auf der BTreeMap verwenden, um den Wert zu extrahieren, der mit den Schlüsseln verbunden ist, die uns interessieren.

Hier ist das Ergebnis:

impl Petrovich { 
    fn new() -> Petrovich { 
     use yaml_rust::yaml::Hash as YamlHash; 

     // Open Rules File (Panics on error) 
     let mut f = fs::File::open("./src/rules.yml").unwrap(); 
     // Create String Buffer and Read to it 
     let mut buffer = String::new(); 
     f.read_to_string(&mut buffer).unwrap(); 
     // Pass Buffer to Yaml and unwrap 
     let rules: &mut Yaml = &mut YamlLoader::load_from_str(&buffer).unwrap()[0]; 
     let rules: &mut YamlHash = match *rules { 
      Yaml::Hash(ref mut hash) => hash, 
      _ => panic!("not a hash"), 
     }; 

     // Return Petrovich with preloaded rules 
     Petrovich { 
      middlename: rules.remove(&Yaml::String("middlename".into())).unwrap(), 
      firstname: rules.remove(&Yaml::String("firstname".into())).unwrap(), 
      lastname: rules.remove(&Yaml::String("lastname".into())).unwrap(), 
     } 
    } 
} 

Beachten Sie, dass dieser Code in Situationen, in denen Sie Ihre Original-Code Panik wird (wenn es funktioniert) würde nicht, wie Sie BadValue s stattdessen bekommen würde. Ich überlasse es Ihnen, Fehler so zu behandeln, wie Sie es brauchen.

2

I umgesetzt kürzlich Serde Unterstützung für YAML: https://github.com/dtolnay/serde-yaml

Serde ist ein leistungsfähiges Serialisierung Rahmen, Rust structs zu und von einer Vielzahl von Formaten konvertiert werden können: JSON, YAML, XML, TomL, Message, Bincode.

ist das komplette Arbeitsbeispiel, das eine Petrovich aus einer Datei rules.yml demonstriert Deserialisieren:

extern crate serde; 
extern crate serde_yaml; 

#[macro_use] 
extern crate serde_derive; 

#[derive(Deserialize)] 
struct Petrovich { 
    middlename: String, 
    firstname: String, 
    lastname: String 
} 

fn main() { 
    let f = std::fs::File::open("test.yml").unwrap(); 
    let p: Petrovich = serde_yaml::from_reader(f).unwrap(); 
    assert_eq!(&p.firstname, "latrasis"); 
} 

Serde unterstützt beliebig kompliziert verschachtelte Typen, so dass die Struktur, die Sie deserialisieren könnte Vektoren enthalten, Karten oder andere Strukturen innerhalb und der Code würde genauso einfach bleiben.

+0

Funktioniert das auf stabilen Rust-Versionen oder nur nachts? – rjh

+1

Dies funktioniert auf stabilem Rust 1.15 und höher. – dtolnay