假设我有一个接受参数来更新用户的函数,如下所示:
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 中处理这个问题的惯用方法是什么?
实现此目的的一种方法是自定义
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()));