Rust 可选可为空参数,取消设置或不执行任何操作

问题描述 投票:0回答:1

假设我有一个接受参数来更新用户的函数,如下所示:

pub struct User {
    pub user_id: Uuid,
    pub nickname: String,
    pub avatar: Option<String>,
}

pub struct UserUpdateProps {
    pub user_id: Uuid,
    pub nickname: Option<String>,
    pub avatar: Option<String>,
}

对于

nickname
Option<String>
工作正常,这意味着如果没有通过昵称 - 无需更改。

对于

avatar
Option<String>
不起作用,因为我们无法处理两种情况 - 取消设置并且不执行任何操作。

在 javascript 中

undefined
意味着什么也不做,
null
未设置值。

在 Rust 中处理这个问题的惯用方法是什么?

rust
1个回答
0
投票

实现此目的的一种方法是自定义

enum
,然后将其用作更新函数的参数:

pub enum AvatarUpdate {
    DoNothing,
    Unset,
    SetTo(String),
}

但是如果你问我,如果你的更新参数都是可选的,那么在我看来,你可能需要几个单一用途的更新函数,而不是一个具有很多选项的函数:

impl User {
    // consuming self and returning a new one makes the methods easy to chain
    pub fn with_nickname(self, new_name: String) -> Self {
        Self {
            nickname: new_name,
            ..self
        }
    }

    // call with `None` to unset avatar; do not call at all if you don't want to change it
    pub fn with_avatar(self, new_avatar: Option<String>) -> Self {
        Self {
            avatar: new_avatar,
            ..self
        }
    }
}

let user1 = User {
    user_id: Uuid::new_v4(),
    nickname: "User1".to_string(),
    avatar: Some("cool_avatar".to_string()),
};

// I assume your `User` type implements `Clone`. If it doesn't, you have
// to either consume your original user or let your `with_x` methods take
// `&mut self` and return `&mut Self` instead
let user2 = user1.clone()
                .with_nickname("User2".to_string())
                .with_avatar(Some("less_cool_avatar".to_string()));

let user3 = user1.clone()
                .with_nickname("User3".to_string())
                .with_avatar(None);

如果需要,这还允许您使用构建器模式来构建用户:

// using the `From` trait for implementing the constructor for the base `User` 
// here as an example because you seem to require at least a nickname.
// You could also implement `Default::default()`, if you have a sensible default
// nickname in mind. (You can technically also just derive `Default` for `User`
// but that gives you all zeros as the default `Uuid`. Implementing `Default`
// by hand gives you the option to assign a new `Uuid` for each `User`).
impl From<String> for User {
    fn from(value: String) -> Self {
        Self {
            user_id: Uuid::new_v4(),
            nickname: value,
            avatar: None,
        }
    }
}

let user1 = User::from("User1".to_string())
                .with_avatar(Some("cool_avatar".to_string()));
© www.soinside.com 2019 - 2024. All rights reserved.