我有一个简单的分类器:

struct Clf {
    x: f64,
}

如果观察值小于x,分类器返回0,如果大于x,分类器返回1.

我想实现这个分类器的调用操作符.但是,函数应该能够将浮点或向量作为参数.对于矢量,输出为0或1的矢量,其大小与输入矢量相同:

let c = Clf { x: 0 };
let v = vec![-1, 0.5, 1];
println!("{}", c(0.5));   // prints 1
println!("{}", c(v));     // prints [0, 1, 1]

在这种情况下,如何编写Fn的实现?

impl Fn for Clf {
    extern "rust-call" fn call(/*...*/) {
        // ...
    }
}

推荐答案

简单的回答是:你不能.至少它不会按你想要的方式工作.我认为展示这一点的最好方法是仔细观察,看看会发生什么,但一般的 idea 是Rust不支持函数重载.

在这个例子中,我们将实现FnOnce,因为Fn需要FnMut,这需要FnOnce.所以,如果我们把这些都分类,我们可以对其他功能特性进行分类.

首先,这是不稳定的,所以我们需要一些特性标志

#![feature(unboxed_closures, fn_traits)]

然后,让我们用impl来做f64:

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

Fn个特征家族的参数是通过一个元组提供的,这就是(f64,)语法;这是一个只有一个元素的元组.

这一切都很好,我们现在可以做到c(0.5),尽管在我们实现其他特征之前,它将消耗c.

现在,让我们在Vec秒内做同样的事情:

impl FnOnce<(Vec<f64>,)> for Clf {
    type Output = Vec<i32>;
    extern "rust-call" fn call_once(self, args: (Vec<f64>,)) -> Vec<i32> {
        args.0
            .iter()
            .map(|&f| if f > self.x { 1 } else { 0 })
            .collect()
    }
}

Rust 1.33 nightly岁之前,你不能直接拨打c(v)甚至c(0.5)(以前工作过);我们会得到一个关于函数类型未知的错误.基本上,这些版本的Rust不支持函数重载.但我们仍然可以使用fully qualified syntax调用函数,其中c(0.5)变成FnOnce::call_once(c, (0.5,)).


不知道你的大局,我想通过给Clf个函数来解决这个问题,如下所示:

impl Clf {
    fn classify(&self, val: f64) -> u32 {
        if val > self.x {
            1
        } else {
            0
        }
    }

    fn classify_vec(&self, vals: Vec<f64>) -> Vec<u32> {
        vals.into_iter().map(|v| self.classify(v)).collect()
    }
}

然后你的用法示例就变成了

let c = Clf { x: 0 };
let v = vec![-1, 0.5, 1];
println!("{}", c.classify(0.5));   // prints 1
println!("{}", c.classify_vec(v)); // prints [0, 1, 1]

实际上,我想把第二个函数设为classify_slice,把&[f64]设为更一般的函数,然后你仍然可以通过引用Vec来使用它:c.classify_slice(&v).

Rust相关问答推荐

通用池类型xsx

如何在rust中有条件地分配变量?

使用 struct 外部的属性来改变 struct 的原始方式

返回的future 不是`发送`

如何使用 list 在Rust for Windows中编译?

在执行其他工作的同时,从共享裁判后面的VEC中删除重复项

如何定义实现同名但返回类型不同的 struct 的函数

Gtk4-rs:将监视器作为gdk::monitor获取,而不是作为glib::对象获取

使用Py03从Rust调用Python函数时的最佳返回类型

应为关联类型,找到类型参数

使用 serde::from_value 反序列化为泛型类型

如何将一个矩阵的列分配给另一个矩阵,纳尔代数?

从字节数组转换为字节元组和字节数组时,为什么 Transmute 会对字节重新排序?

为什么 Rust 字符串没有短字符串优化 (SSO)?

如何从trait方法返回std :: iter :: Map?

Rust 中的自动取消引用是如何工作的?

实现泛型的 Trait 方法中的文字

了解 Rust 闭包:为什么它们持续持有可变引用?

如何将 Rust 中的树状 struct 展平为 Vec<&mut ...>?

判断 is_ok 后重用结果