The issue with this code is the following:
pub fn add(&mut self, user: User) {
self.list.push(user); // Here, you move user to the list. The list owns user
self.index.insert(user.id, &user); // Then, you create a reference of a moved value
}
So, in your UserIndex
struct, you want to store values and references of these values. These are called self-referential structs. With the ownership rules of Rust, you need to use unsafe
Rust code to achieve this. If I were you, I'd think of a different way of implementing your collection following the Rust conventions. For example, you could do something like this:
use std::collections::HashMap;
pub struct User {
pub id: u32,
pub name: String,
}
pub struct UsersIndex {
index: HashMap<u32, User>, // I only use the HashMap. The HashMap owns the User structs
}
impl UsersIndex {
pub fn new() -> Self {
UsersIndex {
index: HashMap::new(),
}
}
pub fn add(&mut self, user: User) {
self.index.insert(user.id, user); // user is moved to the HashMap
}
pub fn get(&self, id: u32) -> Option<&User> { // Instead of a nested reference, we get a regular reference
self.index.get(&id)
}
}
fn main() {
let user = User {
id: 42,
name: "test".to_string(),
};
let mut index = UsersIndex::new();
index.add(user);
match index.get(42) {
Some(usr) => println!("{}", usr.name),
_ => println!("Not Found"),
}
}
Here you can find a playground with this implementation.
EDIT
If you need different HashMaps for the same User
structs depending on the key used, you can use Rc
smart pointers. They allow you to create more than one pointer that owns the same struct. It would look more or less like this:
use std::rc::Rc;
use std::collections::HashMap;
pub struct User {
pub id: u32,
pub name: String,
}
pub struct UsersIndex {
index: HashMap<u32, Rc<User>>, // Keys will be the user indeces
name: HashMap<String, Rc<User>>, // Keys will be the user names
}
impl UsersIndex {
pub fn new() -> Self {
UsersIndex {
index: HashMap::new(),
name: HashMap::new()
}
}
pub fn add(&mut self, user: User) {
// user will be moved, so we copy the keys before that:
let user_id = user.id;
let user_name = user.name.clone();
let user_rc = Rc::new(user); // We create the Rc pointer; user is moved to user_rc
self.index.insert(user_id, user_rc.clone()); // Rc pointers can be cloned
self.name.insert(user_name, user_rc); // user_rc is moved to the self.name HashMap
}
pub fn get(&self, id: u32) -> Option<Rc<User>> {
match self.index.get(&id) {
Some(user) => Some(user.clone()),
None => None
}
}
}
fn main() {
let user = User {
id: 42,
name: "test".to_string(),
};
let mut index = UsersIndex::new();
index.add(user);
match index.get(42) {
Some(usr) => println!("{}", usr.name),
_ => println!("Not Found"),
}
}
Here you can find a playground with this new implementation.
Again, if you also need the User
structs to be mutable, then you'll probably need to use an Rc<RefCell<User>>
(link here).
Hope you find this useful!
unsafe
. If I were you, I'd try to find a different solution for this problem. – isaactfaunsafe
code. I suggest you stick to the HashMap and forget about the vector. Take a look at this playground – Román Cárdenas