< 返回版块

viruscamp 发表于 2022-11-07 20:41

一个泛型函数,接收两个Fn(&T)->()参数,两个参数仅生存期不同,怎么写?

参数 fn_long 和 fn_short 要保证类型仅生存期不同, 内存布局完全相同,所以在满足协变逆变条件时可以赋值。

// 大概是这个意思, 语法是错的
fn lifetime_fn_contravariant<'long, 'short, T, F>(mut fn_long: F<'long>, mut fn_short: F<'short>)
    where 'long: 'short, F: Fn(&T) -> () {
        fn_long = fn_short;
}

评论区

写评论
作者 viruscamp 2022-11-10 12:52

不是用来解决实际问题,仅仅用来验证 Fn 的协变逆变。 写的时候发现类型构造器不能分步骤给出参数,类似于函数柯里化。下面的代码如果能实现,我的问题就可以完全解决:

/// var_type 可以是生存期参数
fn f1<CurryStruct<var_type>: Trait1<var_type, i32>>(arg1: CurryStruct<var_type=&str>, arg2: CurryStruct<var_type=u64>) {}

fn f2<CurryStruct<var_type>: Trait1<var_type, i32>, S1: CurryStruct<var_type=&str>, S2: CurryStruct<var_type=u64>>
(arg1: S1, arg2: S2) {}

--
👇
zkx: 你是想要接收两个变量,且要求如下:

  1. 拥有相同的内存布局
  2. 能够执行特定的功能
  3. 接收的参数 lifetime 有长短限制

Fn、FnMut、FnOnce 都是 trait,实现的 struct 可能各有不同,无法保证相同的内存布局,trait object 可以认为变量拥有相同的内存布局,因为都是以 Box 或者 reference 的形式出现。 但根据前面的回答,貌似无法满足你的需求(虽然个人认为那个方案是没问题的),如此一来就只剩下函数指针了:

不过想了想,总感觉这个场景很怪,建议提供下额外的背景信息,描述想解决什么问题。直接提问怎么才能做到 XXX 可能会绕弯路。

zkx 2022-11-08 21:56

你是想要接收两个变量,且要求如下:

  1. 拥有相同的内存布局
  2. 能够执行特定的功能
  3. 接收的参数 lifetime 有长短限制

Fn、FnMut、FnOnce 都是 trait,实现的 struct 可能各有不同,无法保证相同的内存布局,trait object 可以认为变量拥有相同的内存布局,因为都是以 Box 或者 reference 的形式出现。 但根据前面的回答,貌似无法满足你的需求(虽然个人认为那个方案是没问题的),如此一来就只剩下函数指针了:

fn lifetime_fn_contravariant<'short, 'long: 'short, T>(
    mut fn_long: fn(&'long T),
    fn_short: fn(&'short T),
    long_value: &'long T,
    short_value: &'short T,
) {
    fn_long = fn_short;
    fn_long(long_value);
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn f1() {
        let long = "long";
        {
            let short = "short";
            let fn_long = |value| println!("long: {}", value);
            let fn_short = |value| println!("short: {}", value);
            lifetime_fn_contravariant(fn_long, fn_short, &long, &short);
        }
    }
}

不过想了想,总感觉这个场景很怪,建议提供下额外的背景信息,描述想解决什么问题。直接提问怎么才能做到 XXX 可能会绕弯路。

Paradox 2022-11-08 17:49

函数签名相同也不是同一个类型吧,如果要用到捕获变量的闭包看起来需要用 dyn,如果闭包不捕获变量的话倒是可以用函数指针写签名

fn lifetime_fn_contravariant<'short, 'long: 'short, T: 'long>(mut fn_long: fn(&'long T) -> (), mut fn_short: fn(&'short T) -> ()) {
        fn_long = fn_short;
}

--
👇
viruscamp: 用了 dyn 不能保证内部是同一类型吧

--
👇
biluohc: 你看看这个行不行,注意得dyn, 否则大约得unsafe强转

fn main() {
    let s0 = "lifo0".to_uppercase();
    {
        let s1 = "life1".to_uppercase();
        lifetime_fn_contravariant(
            &|s| println!("{} {}", s0, s),
            &|s| println!("{} {}", s1, s),
            "call".to_lowercase(),
        );
    }
}

fn lifetime_fn_contravariant<'short, 'long: 'short, T>(
    fn_long: &'long dyn Fn(&T),
    mut fn_short: &'short dyn Fn(&T),
    value: T,
) {
    fn_short(&value);
    fn_short = fn_long;
    fn_short(&value)
}
作者 viruscamp 2022-11-08 10:51

用了 dyn 不能保证内部是同一类型吧

--
👇
biluohc: 你看看这个行不行,注意得dyn, 否则大约得unsafe强转

fn main() {
    let s0 = "lifo0".to_uppercase();
    {
        let s1 = "life1".to_uppercase();
        lifetime_fn_contravariant(
            &|s| println!("{} {}", s0, s),
            &|s| println!("{} {}", s1, s),
            "call".to_lowercase(),
        );
    }
}

fn lifetime_fn_contravariant<'short, 'long: 'short, T>(
    fn_long: &'long dyn Fn(&T),
    mut fn_short: &'short dyn Fn(&T),
    value: T,
) {
    fn_short(&value);
    fn_short = fn_long;
    fn_short(&value)
}
biluohc 2022-11-07 22:12

你看看这个行不行,注意得dyn, 否则大约得unsafe强转

fn main() {
    let s0 = "lifo0".to_uppercase();
    {
        let s1 = "life1".to_uppercase();
        lifetime_fn_contravariant(
            &|s| println!("{} {}", s0, s),
            &|s| println!("{} {}", s1, s),
            "call".to_lowercase(),
        );
    }
}

fn lifetime_fn_contravariant<'short, 'long: 'short, T>(
    fn_long: &'long dyn Fn(&T),
    mut fn_short: &'short dyn Fn(&T),
    value: T,
) {
    fn_short(&value);
    fn_short = fn_long;
    fn_short(&value)
}
1 共 5 条评论, 1 页