我可以从 Rust 中的函数指针转换为我的类型吗?

Can I convert to my type from a function pointer in rust?

我正在尝试创建一个结构,该结构环绕另一个板条箱提供的类型别名。类型别名看起来像这样:

type Operation = fn(i32) -> String;

我的包装结构看起来像这样:

struct Wrapper {
    operation: Operation
}

很简单。但是,为了方便开发人员,我希望能够使用 From 特征将 Operation 转换为 Wrapper。所以我添加了这个实现:

impl From<Operation> for Wrapper {
    fn from(operation: Operation) -> Self {
        Self {
            operation
        }
    }
}

但是,当我随后尝试使用该实现时,出现编译器错误:

fn main() {
    let wrapped = Wrapper::from(double);
}

fn double(arg: i32) -> String {
    (arg * 2).to_string()
}
error[E0277]: the trait bound `Wrapper: From<fn(i32) -> String {double}>` is not satisfied                                                                                             
  --> src/main.rs:16:19
   |
16 |     let wrapped = Wrapper::from(double);
   |                   ^^^^^^^^^^^^^ the trait `From<fn(i32) -> String {double}>` is not implemented for `Wrapper`
   |
   = help: the trait `From<fn(i32) -> String>` is implemented for `Wrapper`

如您所见,From 未针对 fn(i32) -> String {double} 实现,但针对 fn(i32) -> String 实现。是否可以让编译器忽略此 {double},或者只能通过在我调用 Wrapper::from 时使用 double as Operation 来完成?

这是因为表达式 double(在 Wrapper::from(double) 中)是 而不是 类型 Operation 的函数指针。它实际上是一个未命名类型的zero-sized值,专门引用函数double。但是,可以将此值强制转换为函数指针。请参阅 the docs 关于 fn 基本类型。

无论您如何切分,您的库的用户总是必须明确地将他们拥有的任何具体函数从他们可以写下的匿名值转换为他们需要的 fn;如果没有 type-coercion,根本无法写下 double(或任何它是什么),并让编译器“看到”裸函数指针。

如果你放弃 From 特性,而是提供你自己的函数(其行为类似于 From),你可以实现你需要的:

type Operation = fn(i32) -> String;

struct Wrapper {
    operation: Operation,
}

impl Wrapper {
    fn wrap(operation: Operation) -> Self {
        Self { operation }
    }
}

fn main() {
    let wrapper = Wrapper::wrap(double);
    println!("{}", (wrapper.operation)(10));
}

fn double(arg: i32) -> String {
    (arg * 2).to_string()
}
20

我的理解是它不适用于 From 特征,因为泛型的类型解析发生在 之前 强制转换,因此 double 还不是它需要的函数指针。

但是如果你有自己的方法,那么从一开始就明确了强制转换目标,并且可以成功地将函数转换为函数指针。

如果你想要使用From特性,你需要在运行之前触发强制.from(),正如您已经意识到的那样:

let wrapper = Wrapper::from(double as Operation);
let wrapper: Wrapper = (double as Operation).into();