Rust无需定义方法匹配可变Rc<RefCell<X>>的实现方案
现有如下Rust代码,需要实现test函数逻辑:如果入参里Rc<RefCell<Foo>>包裹的枚举值是Foo::X变体,就修改其内部x字段的值,否则无操作。
enum Foo { X { x: i32 }, Y { y: String }, } // 若foo为X变体则修改x值,否则无操作 fn test(foo: Rc<RefCell<Foo>>) { panic!("help!") }
目前已知可以通过给Foo实现关联方法的方式完成需求,参考实现如下:
impl Foo { pub fn go(&mut self) { match self { Foo::X { x } => *x = 3, _ => (), } } } fn test(foo: Rc<RefCell<Foo>>) { foo.borrow_mut().go() }
现在需要确认是否可以不定义枚举的关联方法,仅通过全局函数、甚至直接在test函数内内联代码的方式实现相同逻辑。
按如下写法直接传参会触发类型不匹配错误:
fn go(foo: &mut Foo) { match foo { Foo::X { x } => *x = 3, _ => (), } } fn test(foo: Rc<RefCell<Foo>>) { // 类型不匹配 [E0308] 期望`&mut Foo`,实际得到`RefMut<Foo>` go(foo.borrow_mut()) }
完整报错信息:
error[E0308]: mismatched types --> src\main.rs:54:8 | 54 | go(foo.borrow_mut()) | -- ^^^^^^^^^^^^^^^^ | | | | | expected `&mut Foo`, found struct `RefMut` | | help: consider mutably borrowing here: `&mut foo.borrow_mut()` | arguments to this function are incorrect | = note: expected mutable reference `&mut Foo` found struct `RefMut<'_, Foo>`
RefCell::borrow_mut() 的返回值是 RefMut<'_, T> 类型,不是原生可变引用 &mut T。RefMut是RefCell配套的借用守卫智能指针,会在自身离开作用域时自动释放RefCell的可变借用标记,以此实现运行时借用检查规则。
它虽然实现了DerefMut特征,可以被转换为&mut T使用,但Rust的普通函数传参不会自动触发这个解引用转换,因此直接把RefMut传给需要&mut Foo的函数会报类型错误。
之前关联方法的写法可以正常运行,是因为Rust的方法调用语法自带自动解引用强制转换,会自动处理RefMut到&mut Foo的类型转换,不需要手动写转换逻辑。
不需要给Foo定义关联方法,手动完成RefMut到&mut Foo的转换即可,既可以配合全局函数使用,也可以直接内联写匹配逻辑。
方案1:配合全局函数使用
对borrow_mut()返回的RefMut显式解引用后,再取可变引用,就能得到&mut Foo类型的值传入函数:
fn go(foo: &mut Foo) { match foo { Foo::X { x } => *x = 3, _ => (), } } fn test(foo: Rc<RefCell<Foo>>) { go(&mut *foo.borrow_mut()); }
也可以显式调用解引用方法完成转换,效果完全一致:
use std::ops::DerefMut; fn test(foo: Rc<RefCell<Foo>>) { go(&mut foo.deref().borrow_mut().deref_mut()); }
方案2:直接内联逻辑
不需要额外定义函数,在test函数内直接拿到&mut Foo后做模式匹配即可:
fn test(foo: Rc<RefCell<Foo>>) { let foo_ref: &mut Foo = &mut *foo.borrow_mut(); match foo_ref { Foo::X { x } => *x = 3, _ => (), } }
内容的提问来源于stack exchange,提问作者Gal

