【问题标题】:Proper way to implement simple collection in rust在 rust 中实现简单收集的正确方法
【发布时间】:2021-07-07 08:11:04
【问题描述】:

我正在尝试创建一个简单的集合:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=361258962c9a25b953aab2a9e4999cc9

use std::collections::HashMap;

pub struct User {
    pub id: u32,
    pub name: String,
}

pub struct UsersIndex<'a> {
    list: Vec<User>,
    index: HashMap<u32, &'a User>,
}

impl UsersIndex<'_> {
    pub fn new() -> Self {
        UsersIndex {
            list: Vec::new(),
            index: HashMap::new(),
        }
    }

    pub fn add(&mut self, user: User) {
        self.list.push(user);
        self.index.insert(user.id, &user);
    }

    pub fn get(&self, id: u32) -> Option<&&User> {
        self.index.get(&id)
    }
}

但无法修复错误:

使用移动值:user

user 活得不够长

据我了解,我必须拥有User 的所有权,但谷歌并没有帮助我如何做到这一点。 Rust 说我需要实现 Copy 特征,但 User 包含 String 类型的字段。

【问题讨论】:

标签: rust


【解决方案1】:

此代码的问题如下:

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
}

因此,在您的UserIndex 结构中,您希望存储这些值的值和引用。这些被称为自引用结构。使用 Rust 的所有权规则,您需要使用 unsafe Rust 代码来实现这一点。如果我是你,我会想出一种不同的方式来遵循 Rust 约定来实现你的集合。例如,您可以这样做:

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 你可以找到一个有这个实现的游乐场。

编辑

如果您需要根据所使用的密钥为相同的User 结构使用不同的HashMap,您可以使用Rc smart pointers。它们允许您创建多个拥有相同结构的指针。它看起来或多或少是这样的:

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 你可以找到这个新实现的游乐场。 同样,如果您还需要 User 结构是可变的,那么您可能需要使用 Rc&lt;RefCell&lt;User&gt;&gt; (link here)。

希望你觉得这很有用!

【讨论】:

  • 完美的解决方案,直到我需要第二个哈希图来按名称索引
  • 在这种情况下,我建议您使用Rc&lt;User&gt; 智能指针。查看 Rust 文档中的 this post。使用这些,您可以创建拥有相同结构的多个指针。但是,您将无法更改此结构。如果你想改变结构,你可以使用Rc&lt;RefCell&lt;User&gt;&gt;(看看this link)。
  • 我添加了另一个使用Rc智能指针的用例,因此您可以创建多个具有相同User结构的HashMap
  • Rc 的事情很简单,只要你不需要删除元素。然后从所有集合中删除它们就变成了一个相当困难的问题——你需要一个带有值和所有键的结构,并使用带有合适的比较适配器的索引集,这样你就可以从值中获取所有键。我敢肯定有人已经将它实现为板条箱。
猜你喜欢
  • 2011-09-02
  • 1970-01-01
  • 1970-01-01
  • 2012-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多