Я новичок в Rust и, пришедший из мира Java, я хотел играть с чертой Rust, как и с Java интерфейсами. Я представил следующую потребность:
- Я должен быть в состоянии сохранить пользователя (имя, фамилию) где-нибудь (в БД, файл)
- Я могу получить все из них
Я начал определять черту, которую хотел иметь:
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
).
Затем я решил реализовать эту черту с возможностями File (пожалуйста, найдите полный код в конце).
Что меня удивило, так это то, что когда я читаю содержимое файла, у меня объявить self
изменчивым. (вот причина: Почему файл должен быть изменяемым для вызова Read :: read_to_string? )
Это раздражает меня, потому что, если я это сделаю, я должен объявить в черте self
как изменчивый, даже если я читаю данные. Я чувствую, что в этой черте есть утечка деталей реализации.
Я думаю, что мой подход неверен или идиоматизм c в 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");
}