You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Rust的HashMap中存储函数时遭遇expected fn item错误求助

在Rust中将函数存储到HashMap的解决方案

我尝试在Rust中创建一个键为字符串、值为函数的HashMap,代码如下:

use std::collections::HashMap;

fn print_one(x: i32) -> String {
    format!("One: {}", x)
}

fn print_two(x: i32) -> String {
    format!("Two: {}", x)
}

fn print_three(x: i32) -> String {
    format!("Three: {}", x)
}

fn main() {
    let function_map: HashMap<String, fn(i32) -> String> = HashMap::from([
        ("one".into(), print_one),
        ("two".into(), print_two),
        ("tree".into(), print_three)
    ]);
    
    println!("{}", function_map.get("one".into()).unwrap()(1));
    println!("{}", function_map.get("two".into()).unwrap()(1));
    println!("{}", function_map.get("tree".into()).unwrap()(1));
    
    println!("Done");
}

编译时触发错误:

expected fn item, found a different fn item

用Vec存储类似结构可以正常运行,但HashMap会报错,不清楚如何解决。


问题原因

Rust中每个函数都是独立的具体类型(称为fn item),哪怕它们签名完全一致。当使用HashMap::from时,编译器会先推断数组第一个元素的类型——也就是print_one的具体fn item类型,而后面的print_two、print_three是不同的fn item类型,类型不匹配导致报错。

而Vec能正常工作,是因为vec![]或into()会自动寻找所有元素的共同超类型(这里就是函数指针fn(i32)->String),并自动将每个fn item转换为函数指针类型。

解决方法

有三种简单的方式可以修复这个问题:

1. 手动将每个函数转换为函数指针

给每个函数添加as fn(i32)->String强制转换,让数组所有元素类型统一为函数指针:

use std::collections::HashMap;

fn print_one(x: i32) -> String {
    format!("One: {}", x)
}

fn print_two(x: i32) -> String {
    format!("Two: {}", x)
}

fn print_three(x: i32) -> String {
    format!("Three: {}", x)
}

fn main() {
    let function_map: HashMap<String, fn(i32) -> String> = HashMap::from([
        ("one".into(), print_one as fn(i32)->String),
        ("two".into(), print_two as fn(i32)->String),
        ("tree".into(), print_three as fn(i32)->String)
    ]);
    
    println!("{}", function_map.get("one").unwrap()(1));
    println!("{}", function_map.get("two").unwrap()(1));
    println!("{}", function_map.get("tree").unwrap()(1));
    
    println!("Done");
}

2. 显式指定数组的类型

直接告诉编译器数组的元素类型是(String, fn(i32)->String),这样编译器会自动转换每个函数:

use std::collections::HashMap;

fn print_one(x: i32) -> String {
    format!("One: {}", x)
}

fn print_two(x: i32) -> String {
    format!("Two: {}", x)
}

fn print_three(x: i32) -> String {
    format!("Three: {}", x)
}

fn main() {
    let function_map: HashMap<String, fn(i32) -> String> = HashMap::from(
        [
            ("one".into(), print_one),
            ("two".into(), print_two),
            ("tree".into(), print_three)
        ] as [(String, fn(i32)->String); 3]
    );
    
    println!("{}", function_map.get("one").unwrap()(1));
    println!("{}", function_map.get("two").unwrap()(1));
    println!("{}", function_map.get("tree").unwrap()(1));
    
    println!("Done");
}

3. 使用collect代替HashMap::from

利用collect会根据目标类型自动推导转换的特性,将数组转为迭代器后收集到HashMap中:

use std::collections::HashMap;

fn print_one(x: i32) -> String {
    format!("One: {}", x)
}

fn print_two(x: i32) -> String {
    format!("Two: {}", x)
}

fn print_three(x: i32) -> String {
    format!("Three: {}", x)
}

fn main() {
    let function_map: HashMap<String, fn(i32) -> String> = [
        ("one".into(), print_one),
        ("two".into(), print_two),
        ("tree".into(), print_three)
    ].into_iter().collect();
    
    println!("{}", function_map.get("one").unwrap()(1));
    println!("{}", function_map.get("two").unwrap()(1));
    println!("{}", function_map.get("tree").unwrap()(1));
    
    println!("Done");
}

额外说明

如果你的函数需要捕获环境(比如闭包),那函数指针就不够用了,这时候需要使用Box<dyn Fn(i32)->String>来存储,但那是另一种场景,这里你的函数都是无捕获的普通函数,用函数指针完全足够。

内容的提问来源于stack exchange,提问作者Iter Ator

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 04:05:41