< 返回版块

zjhken 发表于 2022-12-20 14:54

Tags:fn,

下面这么写是能跑的, 代码非常简单.

use core::future::Future;

async fn haha(ctx: u8) -> u8 {
    return 2u8;
}

fn set_func<T>(path: &'static str, f: fn(u8) -> T) -> u8
where
    T: Future<Output = u8> + Send + 'static,
{
    return 8u8;
}

fn main() {
  set_func("haha", haha);
}

但是当改了一下haha函数的入参 u8 -> &mut u8, 事情就不一样了

use core::future::Future;

async fn haha(ctx: &mut u8) -> u8 {
    return 2u8;
}

fn set_func<T>(path: &'static str, f: fn(&mut u8) -> T) -> u8
where
    T: Future<Output = u8> + Send + 'static,
{
    return 8u8;
}

fn main() {
  set_func("haha", haha);
}

报错, 说是类型不匹配, 哪位大侠知道这是啥原因? 是从fn pointer变吃fn item了吗? 有没有办法保留这个&mut u8, 让类型检查通过?

   |
13 |     set_func("haha", haha);
   |     --------         ^^^^ one type is more general than the other
   |     |
   |     arguments to this function are incorrect
   |
   = note: expected fn pointer `for<'a> fn(&'a mut u8) -> _`
                 found fn item `for<'a> fn(&'a mut u8) -> impl Future<Output = u8> {haha}`

评论区

写评论
作者 zjhken 2022-12-24 13:38

非常感谢苦瓜大大, 确实解决了. (虽然又遇到了另一个问题, 不过是另外的问题了)

--
👇
苦瓜小仔:

这进入了 HRTB 的主场。

无论你在现有的 set_func 上怎么标注生命周期,都没有用。因为这种情况下,你需要描述 for<'a> fn(&'a mut u8) -> T where T: 'a 约束(注意,这不是 Rust 目前支持的 trait bound 语法)。目前有两种主流 workaround。 playground

一个案例

作者 zjhken 2022-12-21 10:38

感谢Grobycn, 确完两者都可以通过这个例子, 但就如同苦瓜大大所说, 我晚上试了这两个方法, 都无法通过实际的代码, 因为里面要用到haha这个函数时, 生命周期就出来报错了, 搞了好久都没搞过去.

--
👇
Grobycn: 这是生命周期问题, haha 的返回值类型 impl Future<Output = u8>,但是 set_func 要求 f: impl Future<Output = u8> + Send + 'static。 估计是传入的参数 &mut u8 导致编译器无法推断出 'static, 可以手动 desugar 成 也可以修改 set_func 的约束条件

苦瓜小仔 2022-12-21 09:45

编译器complain的其实不是pointer和item的区别, 而是类型细节中的返回值的区别, 一个为_,一个为impl...?

是的。

其他两位的写法可以通过这个例子,但其实很局限。(如果这已经能满足你的要求,那么很好:)


考虑在 set_func 函数内部,把函数内的变量传入 haha,那么你会遇到明确的生命周期的问题 playground

fn set_func<'a, T>(path: &'static str, f: fn(&'a mut u8) -> T) -> u8
where
    T: Future<Output = u8> + Send + 'a,
{
    let mut a = 123;
    f(&mut a);
    return 8u8;
}

error[E0597]: `a` does not live long enough
  --> src/main.rs:12:7
   |
7  | fn set_func<'a, T>(path: &'static str, f: fn(&'a mut u8) -> T) -> u8
   |             -- lifetime `'a` defined here
...
12 |     f(&mut a);
   |     --^^^^^^-
   |     | |
   |     | borrowed value does not live long enough
   |     argument requires that `a` is borrowed for `'a`
13 |     return 8u8;
14 | }
   | - `a` dropped here while still borrowed

这进入了 HRTB 的主场。

无论你在现有的 set_func 上怎么标注生命周期,都没有用。因为这种情况下,你需要描述 for<'a> fn(&'a mut u8) -> T where T: 'a 约束(注意,这不是 Rust 目前支持的 trait bound 语法)。目前有两种主流 workaround。 playground

一个案例

ThalliMega 2022-12-20 19:44

好像加个'a就过了

fn set_func<'a,T>(_path: &'static str, _f: fn(&'a mut u8) -> T) -> u8
where
    T: Future<Output = u8> + Send + 'static,
{
    return 8u8;
}
作者 zjhken 2022-12-20 17:24

苦瓜大大, 能否这么理解, 其实这个haha函数既是fn pointer也是fn item, 而编译器complain的其实不是pointer和item的区别, 而是类型细节中的返回值的区别, 一个为_,一个为impl...?

--
👇
苦瓜小仔: 关于异步函数中的生命周期,请确保完全理解

https://rust-lang.github.io/async-book/03_async_await/01_chapter.html#async-lifetimes

即,异步函数+生命周期是如何解语法糖。


查找概念,首先翻 the Reference

An item is a component of a crate. Items are organized within a crate by a nested set of modules.

Every variable, item, and value in a Rust program has a type. The type of a value defines the interpretation of the memory holding it and the operations that may be performed on the value.

你所定义的 haha 是一个 function item,也是一个function pointer type

因为两者具有如下关系:

Function Pointer types can be created via a coercion from both function items and non-capturing closures.

苦瓜小仔 2022-12-20 15:38

关于异步函数中的生命周期,请确保完全理解

https://rust-lang.github.io/async-book/03_async_await/01_chapter.html#async-lifetimes

即,异步函数+生命周期是如何解语法糖。


查找概念,首先翻 the Reference

An item is a component of a crate. Items are organized within a crate by a nested set of modules.

Every variable, item, and value in a Rust program has a type. The type of a value defines the interpretation of the memory holding it and the operations that may be performed on the value.

你所定义的 haha 是一个 function item,也是一个function pointer type

因为两者具有如下关系:

Function Pointer types can be created via a coercion from both function items and non-capturing closures.

Grobycn 2022-12-20 15:12

这是生命周期问题, haha 的返回值类型 impl Future<Output = u8>,但是 set_func 要求 f: impl Future<Output = u8> + Send + 'static。 估计是传入的参数 &mut u8 导致编译器无法推断出 'static, 可以手动 desugar 成

fn haha(ctx: &mut u8) -> impl Future<Output = u8> + 'static {
    async { 2u8 }
}

也可以修改 set_func 的约束条件

fn set_func<'a, T>(path: &'static str, f: fn(&'a mut u8) -> T) -> u8
where
    T: Future<Output = u8> + Send + 'a,
{
    return 8u8;
}
1 共 8 条评论, 1 页