rust 在这种情况下,我可以使用什么模式或技术?

owfi6suc  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(107)

下面的代码是一个小例子,说明了几天来困扰我的问题: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);
}

rsl1atfo

rsl1atfo1#

如果您需要在创建值时处理None,然后像处理Some一样修改它,那么在Option上使用其中一个get_or_insert*函数可能会受益。
有了它,你可以写这样的代码:

input.condition
    .get_or_insert_with(PlayerConditions::new)
 // .set_id(None) -- can be chained since your methods take in `&mut Self` and return `&mut Self`
 // .set_name(None)
    .set_subscribed_at_is_nil(Some(true));

字符串

0vvn1miw

0vvn1miw2#

简单地说:

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);
}

字符串
是的,它有点长,但它很清楚,我们并不总是需要最短的代码。

相关问题