如何在Rust中组合修改结构体的函数?
组合可变引用函数的生命周期问题解决
问题根因
你使用的compose_two未添加生命周期约束,Rust编译器处理带生命周期的可变引用时会做保守推断——它默认认为闭包可能持有输入的可变引用,于是将返回引用的生命周期与闭包本身绑定。这导致调用baz(&mut x)后,x的可变借用会被baz“持有”到其生命周期结束,直接触发借用检查错误。
修复方案:给compose_two添加生命周期参数
修改compose_two,明确输入引用的生命周期与输出引用完全一致,告诉编译器闭包仅转发借用、不会持有引用:
#[derive(Debug)] struct Foo { a: i32 } macro_rules! compose { ( $last:expr ) => { $last }; ( $head:expr, $($tail:expr), +) => { compose_two($head, compose!($($tail),+)) }; } // 新增生命周期参数'a,约束所有相关类型的生命周期对齐 fn compose_two<'a, A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C + 'a where F: Fn(A) -> B + 'a, G: Fn(B) -> C + 'a, A: 'a, B: 'a, C: 'a, { move |x| g(f(x)) } fn foo(x: &mut Foo) -> &mut Foo { x.a *= 2; x } fn bar(x: &mut Foo) -> &mut Foo { x.a += 1; x } fn main() { let baz = compose!(foo, bar); let mut x = Foo { a: 3 }; let y = baz(&mut x); println!("{:?}", y.a); // 输出7(3*2+1) println!("{:?}", x); // 现在可正常打印x,借用已释放 }
更针对性的写法(可选)
如果仅针对Foo的可变引用场景,可以把约束写得更精准,避免泛型参数过于宽泛:
fn compose_two<'a, F, G>(f: F, g: G) -> impl Fn(&'a mut Foo) -> &'a mut Foo + 'a where F: Fn(&'a mut Foo) -> &'a mut Foo + 'a, G: Fn(&'a mut Foo) -> &'a mut Foo + 'a, { move |x| g(f(x)) }
原理说明
通过生命周期参数'a将闭包、输入函数、输入输出引用的生命周期全部对齐,相当于给编译器明确承诺:闭包不会存储任何引用,调用结束后立即归还借用。编译器拿到这个明确约束后,就不会再假设闭包持有引用,自然能通过借用检查。
内容的提问来源于stack exchange,提问作者Jared Smith
相关产品推荐
相关产品推荐

