下面的代码是困扰我好几天的一个小例子:如何更改这个可变引用结构体的字段值?.
我第一次自己编写一个小应用程序,它遵循“干净的架构”逻辑,就像我对其他语言所做的那样(收集垃圾)。
你能帮我理解我做错了什么以及我可以使用什么更好的模式(我正在考虑构建器模式,但也考虑了各种设置器方法,这些方法允许我在需要时在多个地方更改值) ?
我想要实现的最大的便利就是能够使用这样的代码:
if let Some(condition) = &mut input.condition {
condition.set_subscribed_at_is_nil(true);
} else {
input.condition = Some(PlayerConditions::new().unset_subscribed_at_is_nil());
}
或更好:
input.condition.unwrap_or_default().set_subscribed_at_is_nil(true);
// or maybe input.condition = input.condition.unwrap_or_default().set_subscribed_at_is_nil(true);
if something_happened {
input.condition.unset_it().set_another(Some("something"));
}
if something_else_happened {
input.condition.set_another(Some("something")).check_something();
}
你有什么建议?
代码:
/*
[dependencies]
tokio = { version = "1", features = ["full"] }
*/
#[derive(Debug, Default)]
pub struct PlayerConditions {
id: Option<String>,
name: Option<String>,
subscribed_at_is_nil: Option<bool>,
}
impl PlayerConditions {
pub fn new() -> Self {
Self::default()
}
pub fn is_default(&self) -> bool {
self.id.is_none() && self.name.is_none() && self.subscribed_at_is_nil.is_none()
}
pub fn id(&self) -> &Option<String> {
&self.id
}
pub fn set_id(&mut self, id: Option<String>) -> &mut Self {
self.id = id;
self
}
pub fn name(&self) -> &Option<String> {
&self.name
}
pub fn set_name(&mut self, name: Option<String>) -> &mut Self {
self.name = name;
self
}
pub fn subscribed_at_is_nil(&self) -> &Option<bool> {
&self.subscribed_at_is_nil
}
pub fn set_subscribed_at_is_nil(&mut self, subscribed_at_is_nil: Option<bool>) -> &mut Self {
self.subscribed_at_is_nil = subscribed_at_is_nil;
self
}
}
#[derive(Debug, Default)]
pub struct PlayerListInput {
pub condition: Option<PlayerConditions>,
// This is not String: is a struct in real code
pub order: Option<String>,
// other fields here...
}
#[derive(Debug)]
pub struct DBPlayer {
pub id: String,
pub name: Option<String>,
// For simplicity this is String in this example
pub subscribed_at: Option<String>,
}
impl DBPlayer {
fn query_construction<'a>(
condition: Option<&'a mut PlayerConditions>,
order: &Option<String>,
) -> String {
// Sometimes I need to change condition here (so I need the mutability of it)
let query = "SELECT * FROM players".to_string();
if let Some(condition) = condition {
if !condition.is_default() {
// query = condition.add_each_one_to(query);
}
}
if let Some(_order) = &order {
// add order to query
}
query.to_string()
}
pub async fn query(
_db: &str, // This is not String: is a connection/transaction in real code
input: &mut PlayerListInput,
) -> Vec<DBPlayer> {
// Sometimes I need to change input here (so I need the mutability of it)
let _query = Self::query_construction(input.condition.as_mut(), &input.order);
let players = vec![]; // fetch_them_from_db_using_query_here;
players
}
}
async fn players_from_repo(input: &mut PlayerListInput) -> Vec<DBPlayer> {
// Here I need to change input checking if something is already added, for example:
if let Some(condition) = &mut input.condition {
condition.set_subscribed_at_is_nil(Some(true));
} else {
input.condition = Some(PlayerConditions::new().set_subscribed_at_is_nil(Some(true)));
}
DBPlayer::query( "db_connection", input).await
}
#[tokio::main]
async fn main() {
let mut input = PlayerListInput::default();
let players = players_from_repo(&mut input).await;
// I still need input ownership here
dbg!(input);
dbg!(players);
}
简单来说:
if let Some(condition) = &mut input.condition {
condition.set_subscribed_at_is_nil(Some(true));
} else {
let mut new_condition = PlayerConditions::new();
new_condition.set_subscribed_at_is_nil(Some(true));
input.condition = Some(new_condition);
}
是的,它有点长,但它非常清晰,我们并不总是需要最短的代码。