Generics 如何将带有泛型的闭包传递给函数而不使该函数泛型?

Generics 如何将带有泛型的闭包传递给函数而不使该函数泛型?,generics,rust,closures,Generics,Rust,Closures,我有一个使用枚举的函数来应用二进制函数。这是为口译员准备的: use std::ops::*; #[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum Scalar { I64(i64), I32(i32), //many many others } pub trait TMath: Add + Mul + Sized {} //mark numerical types impl<T: Add + Mul&

我有一个使用枚举的函数来应用二进制函数。这是为口译员准备的:

use std::ops::*;

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum Scalar {
    I64(i64),
    I32(i32),
    //many many others
}

pub trait TMath: Add + Mul + Sized {} //mark numerical types
impl<T: Add + Mul> TMath for T {}

fn add<T: TMath>(x: T, y: T) -> <T as Add>::Output {
    x + y
}

pub type NatBinExpr<T: TMath> = Fn(&T, &T) -> T;
但也要使其适用于任意二进制函数:

let result = bin_op(Scalar::concat, &Scalar::I32(1), &Scalar::I32(2));
然而,我还没有找到一种方法可以在不使用
bin_op
generic的情况下通过闭包:

fn bin_op(apply: &NatBinExpr???, x: &Scalar, y: &Scalar) -> Scalar {
    match (x, y) {
        (Scalar::I64(a), Scalar::I64(b)) => Scalar::I64(apply(a, b)),
        (Scalar::I32(a), Scalar::I32(b)) => Scalar::I32(apply(a, b)),
    }
}
使
bin_op
通用是不对的
bin_op
对标量进行操作,但内部操作是通用的


我最初

谈论函数类型基本上有两种不同的方式:

  • 指针:
    fn(A,B)->C
  • 特征:
    Fn(A,B)->C
    FnMut(A,B)->C
    FnOnce(A,B)->C
在这两种情况下,它们都以参数和结果类型为特征

那么,
apply
的参数和结果类型是什么

视情况而定。

从您的示例中,我们可以看到,
[i64,i32,…]
中的
T
FnOnce(T,T)->T

这不是一种类型,而是多种类型。因此,它需要的不是单一的功能,而是多种功能;或者可能是一个函数对象实现了一次
fn多次


函数对象路由仅在夜间可用,并且需要大量的样板文件(宏会有帮助):

#![特征(fn_特征)]
#![功能(未固定的_闭包)]
使用std::ops::*;
#[派生(调试、克隆、PartialEq、PartialOrd)]
pub枚举标量{
I64(I64),
I32(I32),
//许多其他的
}
pub-trait-TMath:Add+Mul+size{}//标记数字类型
T{}的impl TMath
结构加法器;
加法器的impl-FnOnce{
类型输出=i64;
外部“锈调用”fn调用一次(self,args:(i64,i64))->i64{
参数0+参数1
}
}
加法器的impl-FnMut{
外部“锈调用”fn调用_mut(&mut self,args:(i64,i64))->i64{
参数0+参数1
}
}
加法器的impl-Fn{
外部“锈调用”fn调用(&self,args:(i64,i64))->i64{
参数0+参数1
}
}
加法器的impl-FnOnce{
类型输出=i32;
外部“锈调用”fn调用一次(self,args:(i32,i32))->i32{
参数0+参数1
}
}
加法器的impl-FnMut{
外部“锈调用”fn调用_mut(&mut self,args:(i32,i32))->i32{
参数0+参数1
}
}
加法器的impl-Fn{
外部“锈调用”fn调用(&self,args:(i32,i32))->i32{
参数0+参数1
}
}
fn bin_op(应用:&F,x:Scalar,y:Scalar)->Scalar
哪里
F:Fn(i64,i64)->i64,
F:Fn(i32,i32)->i32,
{
匹配(x,y){
(标量::I64(a),标量::I64(b))
=>标量::I64((应用为&Fn(I64,I64)->I64)(a,b)),
(标量::I32(a),标量::I32(b))
=>标量::I32((应用as&Fn(I32,I32)->I32)(a,b)),
_=>无法访问!(),
}
}
fn main(){
让结果=bin_op(&加法器,标量::I32(1),标量::I32(2));
println!(“{:?}”,结果);
}

看起来您需要更高级的类型来表达这一点,而rust目前不支持这一点。这是冗长还是比正常方式更详细?如果它不能在没有每个操作符实现的情况下崩溃为只通过(A,B)->C,那么我就看不出这有什么意义。@mamcx:它肯定是在冗长的方面,本质上归结为生锈,不支持重载。老实说,在这一点上,我会使用一个定制的
特征BinOp{fn on_i64(&self,i64,i64)->i64;fn on_i32(&self,i32,i32)->i32;…}
(每种类型都有一个方法),然后为
加法器
乘法器
,等等实现它。@mamcx您可能会创建一个
宏规则宏以最小化重复。
fn bin_op(apply: &NatBinExpr???, x: &Scalar, y: &Scalar) -> Scalar {
    match (x, y) {
        (Scalar::I64(a), Scalar::I64(b)) => Scalar::I64(apply(a, b)),
        (Scalar::I32(a), Scalar::I32(b)) => Scalar::I32(apply(a, b)),
    }
}
#![feature(fn_traits)]
#![feature(unboxed_closures)]

use std::ops::*;

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum Scalar {
    I64(i64),
    I32(i32),
    //many many others
}

pub trait TMath: Add + Mul + Sized {} //mark numerical types

impl<T: Add + Mul> TMath for T {}

struct Adder;

impl FnOnce<(i64, i64)> for Adder {
    type Output = i64;
    extern "rust-call" fn call_once(self, args: (i64, i64)) -> i64 {
        args.0 + args.1
    }
}

impl FnMut<(i64, i64)> for Adder {
    extern "rust-call" fn call_mut(&mut self, args: (i64, i64)) -> i64 {
        args.0 + args.1
    }
}

impl Fn<(i64, i64)> for Adder {
    extern "rust-call" fn call(&self, args: (i64, i64)) -> i64 {
        args.0 + args.1
    }
}

impl FnOnce<(i32, i32)> for Adder {
    type Output = i32;
    extern "rust-call" fn call_once(self, args: (i32, i32)) -> i32 {
        args.0 + args.1
    }
}

impl FnMut<(i32, i32)> for Adder {
    extern "rust-call" fn call_mut(&mut self, args: (i32, i32)) -> i32 {
        args.0 + args.1
    }
}

impl Fn<(i32, i32)> for Adder {
    extern "rust-call" fn call(&self, args: (i32, i32)) -> i32  {
        args.0 + args.1
    }
}

fn bin_op<F>(apply: &F, x: Scalar, y: Scalar) -> Scalar
    where
        F: Fn(i64, i64) -> i64,
        F: Fn(i32, i32) -> i32,
{
    match (x, y) {
        (Scalar::I64(a), Scalar::I64(b))
            => Scalar::I64((apply as &Fn(i64, i64) -> i64)(a, b)),
        (Scalar::I32(a), Scalar::I32(b))
            => Scalar::I32((apply as &Fn(i32, i32) -> i32)(a, b)),
        _ => unreachable!(),
    }
}

fn main() {
    let result = bin_op(&Adder, Scalar::I32(1), Scalar::I32(2));
    println!("{:?}", result);
}