英文:
How to implement a trait on a function with generic argument
问题
我正在尝试实现以下代码:
#[derive(Debug, Serialize, Deserialize)]
struct SomeFoo {
name: String,
age: i32,
}
fn test(req: SomeFoo) -> i32 {
println!("Value: {:?}", req);
5
}
fn main() {
let mut handlers = HandlerMap::new();
handlers.add("foobar1", &test);
let payload = r#"
{
"name": "John Doe",
"age": 43
}"#;
let result = handlers.dispatch("foobar1", payload);
println!("Result: {}", result);
}
我尝试了几种方法来允许注册一个函数,然后可以以正确的参数稍后调用它。最有前途的方法是创建一个指定了call_with_json()
方法的trait,然后为类型fn(T) -> i32
实现它。
trait CallHandler {
fn call_with_json(&self, req: &str) -> i32;
}
impl<T> CallHandler for fn(T) -> i32
where
T: DeserializeOwned,
{
fn call_with_json(&self, req: &str) -> i32 {
let req: T = serde_json::from_str(req).expect("bad json");
(self)(req)
}
}
这是完整实现的 playground 链接:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=566e33aab2e3c6d3a090d3b9831a4358
Rust 一直告诉我,对于 fn 项 fn(SomeFoo) -> i32 {test}
,未实现 trait CallHandler
。不确定我漏掉了什么。
英文:
I am trying to achieve the following code:
#[derive(Debug, Serialize, Deserialize)]
struct SomeFoo {
name: String,
age: i32,
}
fn test(req: SomeFoo) -> i32 {
println!("Value: {:?}", req);
5
}
fn main() {
let mut handlers = HandlerMap::new();
handlers.add("foobar1", &test);
let payload = r#"
{
"name": "John Doe",
"age": 43
}"#;
let result = handlers.dispatch("foobar1", payload);
println!("Result: {}", result);
}
I tried a few approaches to allow to register a function that can than be later called with the correct argument. The most promising was to create a trait that specified a method call_with_json()
and then implement it for the type fn(T)
.
trait CallHandler {
fn call_with_json(&self, req: &str) -> i32;
}
impl<T> CallHandler for fn(T) -> i32
where
T: DeserializeOwned,
{
fn call_with_json(&self, req: &str) -> i32 {
let req: T = serde_json::from_str(req).expect("bad json");
(self)(req)
}
}
Here playground link with the full impl.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=566e33aab2e3c6d3a090d3b9831a4358
Rust keeps telling me that the trait CallHandler
is not implemented for fn item fn(SomeFoo) -> i32 {test}
Not sure what I am missing here.
答案1
得分: 1
在Rust中,每个函数都有其自己的“函数项类型”,通常在需要时会被隐式转换为函数指针。函数项类型是零大小的类型;因为它唯一标识单个函数,所以不需要存储任何运行时信息。将其转换为函数指针会丢失有关特定函数的信息,而优先考虑运行时指针。
问题出在你的代码上,Rust不会应用隐式转换来使类型满足trait约束,因为可能有多种不同的转换方式来实现这一点。因此,你需要将转换为函数指针类型的操作明确化以使代码编译:
handlers.add("foobar1", &(test as fn(SomeFoo) -> i32));
(请注意,你还需要删除HandlerMap::add()
的未使用的泛型参数R
。由于它完全没有被使用,编译器将无法推断它。)
一般来说,最好对满足trait约束Fn(T) -> i32
的任何类型进行全局实现。然而,在这种情况下,由于泛型参数T
,这是不可能的,因为在一般情况下无法从实现Fn(T) -> i32
的类型中推断出它。因为类型可能为多个类型T
实现Fn(T) -> i32
(当然,在实践中这可能非常不寻常,但对于编译器来说,重要的是它是可能的。)
英文:
In Rust, each function has its own "function item type", which will usually be implicitly coerced into a function pointer when needed. The function item type is a zero-sized type; since it uniquely identifies a single function, it doesn't need to store any runtime information. Converting it to a function pointer drops the information about the specific function from the compile-time type in favour of a runtime pointer.
The problem with your code is that Rust won't apply implicit coercions to make a type satisfy a trait bound, since there may be various different coercions to do this. So you need to make the cast to the function pointer type explicit to make the code compile:
handlers.add("foobar1", &(test as fn(SomeFoo) -> i32));
(Note that you also need to remove the unused generic parameter R
for HandlerMap::add()
. Since it's completely unused, the compiler will understandably be unable to infer it.)
It would be generally preferable to have a blanket implementation for any type satisfying the trait bound Fn(T) -> i32
. However, that's not possible in this case due to the generic parameter T
, which is impossible to infer from the type implementing Fn(T) -> i32
in the general case, since a type might implement Fn(T) -> i32
for multiple types T
. (Of course that would be very unusual in practice, but all that matters for the compiler is that it's possible.)
答案2
得分: 0
我实际上成功找到了一种方法,可以在不使用 as F(_)
的情况下实现,尽管它需要在堆上创建一个闭包。
英文:
I actually managed to find a way to this without the as F(_)
bit, though it requires a closure on the heap.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论