0

I want to write a simple key/value store that can store anything. I started with a small wrapper around a HashMap:

use std::any::{Any, TypeId};
use std::collections::HashMap;

#[derive(Debug)]
struct Pair<'a> {
    key: &'a str,
    value: Box<Any>,
    data_type: TypeId,
}

impl<'a> Pair<'a> {
    fn new<T>(k: &'a str, v: T) -> Self
    where
        T: Any + 'static,
    {
        Self {
            key: k,
            value: Box::new(v),
            data_type: TypeId::of::<T>(),
        }
    }

    fn update<T>(&mut self, new_value: T)
    where
        T: Any + 'static,
    {
        self.data_type = TypeId::of::<T>();
        self.value = Box::new(new_value);
    }

    fn get<T>(&'a self) -> &'a T
    where
        T: Any + 'static,
    {
        self.value.downcast_ref::<T>().unwrap()
    }

    fn get_mut<T>(&'a mut self) -> &'a mut T
    where
        T: Any + 'static,
    {
        self.value.downcast_mut::<T>().unwrap()
    }
}

#[derive(Debug)]
struct Database<'a> {
    data: HashMap<&'a str, Pair<'a>>,
}

impl<'a> Database<'a> {
    fn new() -> Self {
        Self {
            data: HashMap::new(),
        }
    }

    fn insert(&mut self, data: Pair<'a>) {
        self.data.insert(data.key, data);
    }

    fn find(&self, key: &str) -> &'a Pair {
        self.data.get(key).unwrap()
    }

    fn find_mut(&mut self, key: &str) -> &'a mut Pair {
        self.data.get_mut(key).unwrap()
    }

    fn remove(&mut self, key: &str) {
        self.data.remove(key);
    }
}

#[derive(Debug)]
struct Position {
    x: f32,
    y: f32,
}

fn main() {
    let mut db = Database::new();

    // add data
    {
        let pair1 = Pair::new("testkey", "Awesome string...".to_owned());
        let pair2 = Pair::new("position", Position { x: 0.0, y: 0.0 });

        db.insert(pair1);
        db.insert(pair2);
    }

    // change data
    {
        let pair = db.find_mut("position");
        pair.get_mut::<Position>().x = 50.0;
    } // <--- end of &mut Pair

    // read data
    let pos = db.find("position");

    println!("{:?}", pos);
}
error[E0502]: cannot borrow `db` as immutable because it is also borrowed as mutable
   --> src/main.rs:101:15
    |
96  |         let pair = db.find_mut("position");
    |                    -- mutable borrow occurs here
...
101 |     let pos = db.find("position");
    |               ^^
    |               |
    |               immutable borrow occurs here
    |               mutable borrow later used here

I don't understand the borrow checker here. I scoped everything so pair don't exist by db.find("position"). Why does it not work? If I understand the documentation correctly, it's to use mutable vars in a nested scope.

I wrote a simpler example, I came from:

fn main() {
    let mut x = 5;

    {
        let y = &mut x;
        *y = 10;
    }

    println!("{}", x);
}

This works as expected. I'm really stuck with the borrow checker.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
createproblem
  • 1,542
  • 16
  • 30

1 Answers1

2

TL;DR

fn get<T>(&self) -> &T
fn get_mut<T>(&mut self) -> &mut T

fn find(&self) -> &Pair<'a>
fn find_mut(&mut self) -> &mut Pair<'a>

Creating a MCVE is a vitally important skill to being an effective programmer. We catalog Rust-specific techniques on the tag wiki. Here's one for your code:

#[derive(Debug, Default)]
struct Pair<'a>(&'a str);

#[derive(Debug, Default)]
struct Database<'a> {
    data: &'a str,
}

impl<'a> Database<'a> {
    fn find(&self) -> &'a Pair {
        unimplemented!()
    }

    fn find_mut(&mut self) -> &'a mut Pair {
        unimplemented!()
    }
}

fn main() {
    let mut db = Database::default();
    {
        db.find_mut();
    }
    db.find();
}

The problem occurs because you've sprinkled lifetimes about without them being correct. Specifically:

fn find(&self) -> &'a Pair
fn find_mut(&mut self) -> &'a mut Pair

These methods say that they are going to return a reference to a Pair that lasts as long as the data you inserted into the Database. This cannot be true because the data you've inserted is a &'static str.

You actually want:

fn find(&self) -> &Pair<'a>
fn find_mut(&mut self) -> &mut Pair<'a>

Adding #![deny(rust_2018_idioms)] to your crate helps catch these, although the error messages aren't perfect yet:

error: hidden lifetime parameters in types are deprecated
  --> src/main.rs:12:27
   |
12 |     fn find(&self) -> &'a Pair {
   |                           ^^^^- help: indicate the anonymous lifetime: `<'_>`

error: hidden lifetime parameters in types are deprecated
  --> src/main.rs:16:39
   |
16 |     fn find_mut(&mut self) -> &'a mut Pair {
   |                                       ^^^^- help: indicate the anonymous lifetime: `<'_>`

If we then expand back out to the complete original problem, we see that the problem still hasn't gone away. Since we just had an issue with the lifetime inside of Pair, let's look to see if there are any more related issues:

fn get<T>(&'a self) -> &'a T
fn get_mut<T>(&'a mut self) -> &'a mut T

This states that self will live as long as self.key, which is not what you want 99% of the time. Remove these explcit lifetimes to allow normal lifetime elision to take place:

fn get<T>(&self) -> &T
fn get_mut<T>(&mut self) -> &mut T

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Thanks for the explanation, I think I've to read the chapter about lifetimes one more time. – createproblem Feb 26 '19 at 16:05
  • When I read `deny rust_2018_idioms` I am thinking that those idioms are now denied, when actually it's enforcing them. Hum... :D – Matthieu M. Feb 27 '19 at 12:55
  • @MatthieuM. yeah, I don't like it either. I feel like I've heard some argument about why it makes sense with this polarity, but I don't recall it now. – Shepmaster Feb 27 '19 at 13:10