4

I am new to Rust and I'm working on a game with the Piston engine to wet my feet.

I want to render a bunch of entities using sprite sheets, but a lot of entities might share a sprite sheet so I would like to only load and store one copy of each file.

In pseudo code, my approach is basically like this:

fn get_spritesheet(hashmap_cache, file):
    if file not in hashmap_cache:
        hashmap_cache[file] = load_image_file(file)

    return hashmap_cache[file]

Then maybe something like:

//These 'sprite' fileds point to the same image in memory
let player1 = Entity { sprite: get_spritesheet(cache, "player.png") };
let player2 = Entity { sprite: get_spritesheet(cache, "player.png") }; 

However, I am running in to a lot of barriers with Rust's ownership system (probably because I just don't understand it).

As far as I can tell, I want the cahe/hashmap to "own" the image resources. Specifically, then, returning references (as in the get_spritesheet function) seems to be weird. Also, is it possible for a struct to not own all its members? I think it is but I was confused about how to do that.

QuinnFreedman
  • 2,242
  • 2
  • 25
  • 42

2 Answers2

6

This is what std::collections::hash_map::Entry is for:

A view into a single entry in a map, which may either be vacant or occupied.

match hashmap_cache.entry(key) {
    Entry::Vacant(entry) => *entry.insert(value),
    Entry::Occupied(entry) => *entry.get(),
}

If the entry is vacant, the value is inserted into the cache, otherwise the value is received from the cache.

Playground

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Tim Diekmann
  • 7,755
  • 11
  • 41
  • 69
0

I figured out that returning a borrow was only giving me an error because I passed in two borrowed references to the function and it didn't know which one the return was borrowing from. Adding lifetime parameters made it work. My function now looks like this:

pub fn load_spritesheet<'a>(
    spritesheets: &'a mut HashMap<String, Texture>,
    file: &String,
) -> Option<&'a Texture> {
    if !spritesheets.contains_key(file) {
        if let Some(texture) = load_texture(
            &vec!["dynamic_entities", "spritesheets"],
            file.as_str(),
            &TextureSettings::new(),
        ) {
            spritesheets.insert(file.clone(), texture);
        }
    }
    spritesheets.get(file)
}

As for the reference in the struct, I just implemented a getter method that calls load_spritesheet every frame. Not ideal, but works for now.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
QuinnFreedman
  • 2,242
  • 2
  • 25
  • 42