如何用堆引用惯用构造struct?

时间:2016-02-03 06:27:58

标签: rust

所以我目前通过将struct文件加载到堆内存中而难以初始化Yaml

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"]
        }
    }
}

但是我收到cannot move out of indexed content错误。我该如何解决这个问题?

编辑:我正在使用yaml-rust,并在rules.yml中包含firstnamelastnamemiddlename字段的文件./src

可在此处找到来源:https://github.com/Latrasis/petrovich-rs

3 个答案:

答案 0 :(得分:3)

您正试图摆脱借来的指针(因为rules[str]返回&Yaml),但这不合法。我们需要使用允许我们将值移出主Yaml对象的函数。这将改变主Yaml对象,但这可以,因为我们将在函数结束时丢弃它。

首先,我们需要了解字符串索引的作用。如果值是哈希值,则The implementation仅返回一个重要值,并通过构造Yaml::String值来索引哈希值来获取基础值。

Yaml::Hash变体包裹BTreeMap<Yaml, Yaml>。该库提供了as_hash便捷方法来访问它,但这只提供了一个不可变的指针。我们需要使用模式匹配来获取可变指针。

接下来,我们将使用BTreeMap上的remove method来提取与我们感兴趣的密钥相关联的值。

结果如下:

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(),
        }
    }
}

请注意,在您的原始代码(如果有效)不会出现的情况下,此代码会出现混乱,因为您会获得BadValue。我会留给你处理你需要的错误。

答案 1 :(得分:2)

YamlClone,因此您只需在引用上调用.clone()即可复制其内容&#34; out&#34;:

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

我更喜欢这个被接受的解决方案,因为被复制的数据很小,并且它不依赖于yaml::Hash的实现。克隆整个文档树可能是另一回事。

这里可能有一个参数可以添加到yaml-rust中。将元素移出树是一个合理的请求,但API不直接支持它。

答案 2 :(得分:2)

我最近对YAML实施了Serde支持:https://github.com/dtolnay/serde-yaml

Serde是一个功能强大的序列化框架,允许将Rust结构转换为各种格式:JSON,YAML,XML,TOML,MessagePack,Bincode。

这是一个完整的工作示例,演示了从文件rules.yml反序列化Petrovich:

Comment

Serde支持任意复杂的嵌套类型,因此反序列化的结构可以包含向量,映射或其他结构,代码将保持简单。