Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to idiomatically construct struct with heap references?

Tags:

rust

So i'm currently stumped on initializing a struct by loading a Yaml file into heap memory:

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

However i get a cannot move out of indexed content error. How do i tackle this?

EDIT: I am using yaml-rust for this and have a file rules.yml with firstname, lastname and middlename fields included within ./src

Source can be found here: https://github.com/Latrasis/petrovich-rs

like image 710
latrasis Avatar asked Feb 03 '16 06:02

latrasis


1 Answers

You are trying to move out of a borrowed pointer (because rules[str] returns a &Yaml), but that's not legal. We need to use functions that allow us to move a value out of the main Yaml object. This will alter the main Yaml object, but that's OK here, since we will discard it by the end of the function.

First, we need to understand what indexing with a string does. The implementation only returns a significant value if the value is a hash and gets to the underlying value by constructing a Yaml::String value to index the hash.

The Yaml::Hash variant wraps a BTreeMap<Yaml, Yaml>. The library provides the as_hash convenience method to access it, but this only gives an immutable pointer. We need to use pattern matching to get a mutable pointer.

Next, we'll use the remove method on the BTreeMap to extract the value associated with the keys that interest us.

Here's the result:

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

Note that this code will panic in situations in which your original code (if it worked) wouldn't, as you would get BadValues instead. I'll leave it to you to handle errors as you need.

like image 184
Francis Gagné Avatar answered Oct 22 '22 22:10

Francis Gagné