gpt4 book ai didi

rust - 在 Rust 特征中公开实现细节

转载 作者:行者123 更新时间:2023-12-03 11:46:05 25 4
gpt4 key购买 nike

我是 Rust 的新手,并且来自 Java 世界,我想像使用 Java 接口(interface)一样使用 Rust trait。
我想象了以下需求:

  • 我必须能够将用户(名字、姓氏)保存在某处(在数据库、文件中)
  • 我可以全部获取

  • 我开始定义我想要拥有的特质:

    trait UserDb {
    fn get_all(&self) -> Result<Vec<User>, io::Error>;

    fn save(&mut self, user: &User) -> Result<(), io::Error>;
    }

    你可以看到,当我声明 get_all函数,我没有提到需要在 self 上进行可变借用(即 &mut self )。

    然后我决定用文件功能来实现这个特性(请在最后找到完整的代码)。

    令我惊讶的是,当我阅读文件内容时,我必须声明 self作为可变的。 (原因如下: Why does a File need to be mutable to call Read::read_to_string?)

    这让我很恼火,因为如果我这样做,我必须在 trait self 中声明作为可变的,即使我正在读取数据。我觉得特征中存在实现细节的泄漏。

    我认为我的方法在 Rust 中无效或不惯用。您将如何实现这一目标?

    这是完整的代码:

    ///THIS CODE DOESNT COMPILE
    ///THE COMPILER TELLS TO MAKE self AS MUTABLE
    use std::fs::File;
    use std::fs::OpenOptions;
    use std::io;
    use std::path::Path;
    use std::io::Read;
    use std::io::Write;

    struct User {
    pub firstname: String,
    pub lastname: String,
    }

    trait UserDb {
    fn get_all(&self) -> Result<Vec<User>, io::Error>;

    fn save(&mut self, user: &User) -> Result<(), io::Error>;
    }

    struct FsUserDb {
    pub file: File,
    }

    impl FsUserDb {
    fn new(filename: &str) -> Result<FsUserDb, io::Error> {
    if Path::new(filename).exists() {
    let file = OpenOptions::new()
    .append(true)
    .write(true)
    .open(filename)?;

    Ok(FsUserDb { file })
    } else {
    Ok(FsUserDb {
    file: File::create(filename)?,
    })
    }
    }
    }

    impl UserDb for FsUserDb {
    fn get_all(&self) -> Result<Vec<User>, io::Error> {
    let mut contents = String::new();

    self.file.read_to_string(&mut contents)?;

    let users = contents
    .lines()
    .map(|line| line.split(";").collect::<Vec<&str>>())
    .map(|split_line| User {
    firstname: split_line[0].to_string(),
    lastname: split_line[1].to_string(),
    })
    .collect();

    Ok(users)
    }

    fn save(&mut self, user: &User) -> Result<(), io::Error> {
    let user_string =
    format!("{},{}", user.firstname, user.lastname);

    match self.file.write(user_string.as_bytes()) {
    Ok(_) => Ok(()),
    Err(e) => Err(e)
    }
    }
    }

    fn main() {
    let db = FsUserDb::new("/tmp/user-db");
    }

    最佳答案

    read需要可变借用,对此您无能为力。

    为了解决您的问题,我可以想到三个选项:

  • 将特征上的签名修改为 &mut self正如编译器所建议的那样。这是最清晰的解决方案,我不确定您为什么不喜欢它。
  • 使用内部可变性,如 RefCell并获得可变的 File你需要它的地方。使用此解决方案,您甚至不需要声明 save作为可变的,但会增加一些运行时成本。我推荐阅读 RefCell因为这可能会在以后引入其他类型的错误。
  • 存储文件名,而不是 File处理程序本身并在适当时打开/关闭它。有了这个,你也可以使用不可变的 save .
  • 关于rust - 在 Rust 特征中公开实现细节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60124797/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com