如何修改Rust中可变引用结构体的字段值?
Rust 修改可变引用结构体字段的问题
问题场景
在项目的query_construction函数中,参数类型为Option<&'a mut PlayerConditions>,需要按以下逻辑修改字段:
- 若
condition为None,添加一个设置了set_email(None)的PlayerConditions实例 - 若
condition为Some且email为None,设置set_email(None) - 若
condition为Some且email为Some,不做任何操作
尝试了以下代码:
condition = Some( &mut condition .unwrap_or(&mut PlayerConditions::new()) .set_email(None), );
出现编译错误:
error[E0384]: cannot assign to immutable argument `condition` --> src\main.rs:72:9 | 59 | condition: Option<&'a mut PlayerConditions>, | --------- help: consider making this binding mutable: `mut condition` ... 72 | / condition = Some( 73 | | &mut condition 74 | | .unwrap_or(&mut PlayerConditions::new()) 75 | | .set_email(None), 76 | | ); | |_________^ cannot assign to immutable argument
不想将参数设为可变绑定,希望直接修改可变引用指向的结构体字段,完整代码如下:
#[derive(Debug, Default)] pub struct PlayerConditions { id: Option<String>, name: Option<String>, email: Option<String>, } impl PlayerConditions { pub fn new() -> Self { Self::default() } pub fn is_default(&self) -> bool { self.id.is_none() && self.name.is_none() && self.email.is_none() } pub fn id(&self) -> &Option<String> { &self.id } pub fn set_id(mut self, id: Option<String>) -> Self { self.id = id; self } pub fn name(&self) -> &Option<String> { &self.name } pub fn set_name(mut self, name: Option<String>) -> Self { self.name = name; self } pub fn email(&self) -> &Option<String> { &self.email } pub fn set_email(mut self, email: Option<String>) -> Self { self.email = email; self } } #[derive(Debug, Default)] pub struct PlayerListInput { pub condition: Option<PlayerConditions>, pub order: Option<String>, // This is not String: is a struct in real code // other fields here... } #[derive(Debug)] pub struct DBPlayer { pub id: String, pub name: Option<String>, pub email: Option<String>, } impl DBPlayer { fn query_construction<'a>( condition: Option<&'a mut PlayerConditions>, order: &Option<String>, ) -> String { let mut query = "SELECT * FROM players".to_string(); // --->>>> I need to change condition here using this logic: // if condition is None add one condition with "set_email(None)" // if condition is Some and email == None set "set_email(None)" // if condition is Some and email == Some do nothing // I tried the below but it doesn't work: // condition = Some( // &mut condition // .unwrap_or(&mut PlayerConditions::new()) // .set_email(None), // ); // From here it's ok 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( _session: &str, // This is not String: is a struct in real code _db: &str, // This is not String: is a connection/transaction in real code input: &mut PlayerListInput, ) -> Vec<DBPlayer> { // Check session here 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> { DBPlayer::query("session", "db_connection", input).await } #[tokio::main] async fn main() { let mut input = PlayerListInput::default(); let players = players_from_repo(&mut input).await; dbg!(players); }
解决方法
1. 修复Setter方法的所有权问题
当前的set_email等方法是消费自身并返回新实例,无法直接修改原引用的结构体。需要将这些方法改为可变引用方法,直接修改原对象:
impl PlayerConditions { // ... 保留其他方法 ... pub fn set_id(&mut self, id: Option<String>) -> &mut Self { self.id = id; self } pub fn set_name(&mut self, name: Option<String>) -> &mut Self { self.name = name; self } pub fn set_email(&mut self, email: Option<String>) -> &mut Self { self.email = email; self } }
2. 调整参数类型以支持修改外部Option
原参数Option<&mut PlayerConditions>只能修改已存在的PlayerConditions实例,但无法将外部的None改为Some。需要将参数改为&mut Option<PlayerConditions>,这样才能直接操作外部的Option:
修改query_construction函数的参数和逻辑:
impl DBPlayer { fn query_construction<'a>( condition: &'a mut Option<PlayerConditions>, order: &Option<String>, ) -> String { let mut query = "SELECT * FROM players".to_string(); // 实现需求逻辑 match condition { None => { // 创建新实例并设置email为None *condition = Some(PlayerConditions::new().set_email(None)); } Some(cond) => { if cond.email().is_none() { // email已经是None,执行设置操作(逻辑上可省略,保留以明确需求) cond.set_email(None); } // email是Some时不做操作 } } // 后续逻辑保持不变 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() } // 修改query方法中调用query_construction的方式 pub async fn query( _session: &str, _db: &str, input: &mut PlayerListInput, ) -> Vec<DBPlayer> { // Check session here let _query = Self::query_construction(&mut input.condition, &input.order); let players = vec![]; players } }
逻辑说明
- 当外部的
condition是None时,直接创建新的PlayerConditions实例并赋值给外部的Option - 当外部的
condition是Some时,检查email字段:如果是None则执行设置操作,是Some则跳过 - 修改后的参数类型
&mut Option<PlayerConditions>允许直接修改外部的Option状态,同时修改内部结构体的字段
内容的提问来源于stack exchange,提问作者Fred Hors
相关产品推荐
相关产品推荐

