下面的代码是一个小例子,说明了几天来困扰我的问题:How to change this mutable-referenced struct's field value?。
我第一次自己写一个小应用程序,它遵循“干净的架构”逻辑,就像我在其他语言中所做的那样(垃圾收集)。
你能帮助我理解我做错了什么,以及我可以使用什么更好的模式吗(我正在考虑构建器模式,但也有各种setter方法,允许我在需要时在几个地方更改值)?
我想实现的最大便利是能够使用这样的代码:
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);
}
型
2条答案
按热度按时间rsl1atfo1#
如果您需要在创建值时处理
None
,然后像处理Some
一样修改它,那么在Option
上使用其中一个get_or_insert*
函数可能会受益。有了它,你可以写这样的代码:
字符串
0vvn1miw2#
简单地说:
字符串
是的,它有点长,但它很清楚,我们并不总是需要最短的代码。