Rust中如何在Trait函数中返回Fn闭包或fn函数?
问题描述
我在编写返回Fn(&bool, &bool) -> bool的Rust Trait函数时遇到了问题。我想要实现的函数如下:
fn foo(op: &Operation) -> impl Fn(&bool,&bool) -> bool { let one_of_four = match (op.2.0[0], op.2.0[1]) { (false, false) => |&x: &bool,&y: &bool| !x && y, (false, true) => |&x: &bool,&y: &bool| x && !y, (true, false) => |&x: &bool,&y: &bool| x != y, (true, true) => |&x: &bool,&y: &bool| x && y }; match op.2.0[2] { false => |x: &bool, y: &bool| one_of_four(x, y), true => |x: &bool, y: &bool| !one_of_four(x, y), } }
其中foo_part_1函数在Trait外可正常运行:
fn foo_part_1(op: &Operation) -> impl Fn(&bool,&bool) -> bool { match (op.2.0[0], op.2.0[1]) { (false, false) => |&x: &bool,&y: &bool| !x && y, (false, true) => |&x: &bool,&y: &bool| x && !y, (true, false) => |&x: &bool,&y: &bool| x != y, (true, true) => |&x: &bool,&y: &bool| x && y } }
我希望将foo作为OpTrait的Trait函数,为Negation和Operation实现该Trait,但遇到以下问题:
- Trait中无法使用
impl返回类型; - 尝试用泛型参数
F定义后,为Negation实现时出现类型不匹配错误,提示每个闭包类型唯一,无法匹配调用者指定的F; - 尝试使用
fn类型,虽能返回指定函数,但无法实现函数反转,报错无法对fn类型应用!运算符; - 按照编译器建议改用
Box<dyn Fn>,普通函数中可运行,但移入Trait实现后,match分支出现类型不兼容错误,提示不同分支的闭包类型不兼容。
请问如何在Rust的Trait函数中返回Fn闭包,或实现从函数返回fn类型?
解决方案
1. 统一使用Box<dyn Fn(&bool, &bool) -> bool>返回类型
在Trait中定义返回类型为Box<dyn Fn(&bool, &bool) -> bool>,实现时为每个闭包分支加上Box::new(),确保所有分支返回相同的trait object类型:
步骤1:定义Trait
trait OpTrait { fn foo(&self) -> Box<dyn Fn(&bool, &bool) -> bool>; }
步骤2:为Operation实现Trait
// 假设Operation的定义如下(根据代码推断) struct Operation((), ([bool; 3],)); impl OpTrait for Operation { fn foo(&self) -> Box<dyn Fn(&bool, &bool) -> bool> { let one_of_four = match (self.2.0[0], self.2.0[1]) { (false, false) => Box::new(|&x, &y| !x && y) as Box<dyn Fn(&bool, &bool) -> bool>, (false, true) => Box::new(|&x, &y| x && !y), (true, false) => Box::new(|&x, &y| x != y), (true, true) => Box::new(|&x, &y| x && y), }; match self.2.0[2] { false => Box::new(move |x, y| one_of_four(x, y)), true => Box::new(move |x, y| !one_of_four(x, y)), } } }
注意要点:
- 每个闭包都用
Box::new()包裹,统一转换为Box<dyn Fn(...)>类型; - 内层闭包需要使用
move关键字捕获one_of_four,确保变量能随闭包离开当前作用域。
2. 使用自定义枚举封装闭包类型
如果不想使用动态分发(避免Box的开销),可以定义枚举类型包含所有可能的闭包变体,再为枚举实现Fn(&bool, &bool) -> bool trait:
步骤1:定义枚举并实现Fn trait
enum BoolOp { NotXAndY(Box<dyn Fn(&bool, &bool) -> bool>), XAndNotY(Box<dyn Fn(&bool, &bool) -> bool>), XNotEqualY(Box<dyn Fn(&bool, &bool) -> bool>), XAndY(Box<dyn Fn(&bool, &bool) -> bool>), Negated(Box<dyn Fn(&bool, &bool) -> bool>), } impl Fn(&bool, &bool) -> bool for BoolOp { extern "rust-call" fn call(&self, args: (&bool, &bool)) -> bool { let (x, y) = args; match self { BoolOp::NotXAndY(f) => f(x, y), BoolOp::XAndNotY(f) => f(x, y), BoolOp::XNotEqualY(f) => f(x, y), BoolOp::XAndY(f) => f(x, y), BoolOp::Negated(f) => !f(x, y), } } } impl FnMut(&bool, &bool) -> bool for BoolOp { extern "rust-call" fn call_mut(&mut self, args: (&bool, &bool)) -> bool { (*self).call(args) } } impl FnOnce(&bool, &bool) -> bool for BoolOp { type Output = bool; extern "rust-call" fn call_once(self, args: (&bool, &bool)) -> bool { self.call(args) } }
步骤2:在Trait中返回枚举类型
trait OpTrait { fn foo(&self) -> BoolOp; } impl OpTrait for Operation { fn foo(&self) -> BoolOp { let base_op = match (self.2.0[0], self.2.0[1]) { (false, false) => BoolOp::NotXAndY(Box::new(|&x, &y| !x && y)), (false, true) => BoolOp::XAndNotY(Box::new(|&x, &y| x && !y)), (true, false) => BoolOp::XNotEqualY(Box::new(|&x, &y| x != y)), (true, true) => BoolOp::XAndY(Box::new(|&x, &y| x && y)), }; match self.2.0[2] { false => base_op, true => BoolOp::Negated(Box::new(move |x, y| base_op.call((x, y)))), } } }
这种方式避免了动态分发的间接调用,但代码量较大,适合对性能要求较高的场景。
3. 预定义所有可能的函数,结合Box<dyn Fn>返回
如果逻辑可以拆解为固定的几个静态函数,可预定义这些函数,再结合Box<dyn Fn>处理反转逻辑:
步骤1:预定义基础函数和反转逻辑
fn not_x_and_y(&x: &bool, &y: &bool) -> bool { !x && y } fn x_and_not_y(&x: &bool, &y: &bool) -> bool { x && !y } fn x_not_equal_y(&x: &bool, &y: &bool) -> bool { x != y } fn x_and_y(&x: &bool, &y: &bool) -> bool { x && y } fn negate_op(f: fn(&bool, &bool) -> bool) -> impl Fn(&bool, &bool) -> bool { move |x, y| !f(x, y) }
步骤2:在Trait中实现函数返回
trait OpTrait { fn foo(&self) -> Box<dyn Fn(&bool, &bool) -> bool>; } impl OpTrait for Operation { fn foo(&self) -> Box<dyn Fn(&bool, &bool) -> bool> { let base_fn: fn(&bool, &bool) -> bool = match (self.2.0[0], self.2.0[1]) { (false, false) => not_x_and_y, (false, true) => x_and_not_y, (true, false) => x_not_equal_y, (true, true) => x_and_y, }; match self.2.0[2] { false => Box::new(base_fn), true => Box::new(negate_op(base_fn)), } } }
关键说明
- Rust中每个闭包都是唯一的匿名类型,返回多个闭包分支时必须统一类型,
Box<dyn Fn>是最直接的实现方式; - Trait中不能使用
impl Trait作为返回类型(关联类型要求每个实现返回相同具体类型,闭包类型无法满足); - 函数指针
fn(...)只能指向静态函数,无法捕获环境,因此无法直接用于需要捕获变量的场景(比如反转后的逻辑)。
内容的提问来源于stack exchange,提问作者W Ablout
相关产品推荐
相关产品推荐

