< 返回版块

yuyidegit 发表于 2024-03-21 16:15

trait A<'a>: Sized {
    fn from_i(a: &'a i32) -> Self;
}

struct B<'a> {
    a: &'a i32,
}

impl<'a> A<'a> for B<'a> {
    fn from_i(a: &'a i32) -> Self {
        B { a }
    }
}

fn test<F, T>(f: F)
where
    F: for<'a> Fn(T) -> i32,
    T: for<'a> A<'a>,
{
    let a = 1;
    f(T::from_i(&a));
}

fn test1<'a>(b: B<'a>) -> i32 {
    *b.a
}

fn main() {
    test(test1);
}

目前test(test1) 会报错类型B实现的A trait不够general enough,我应该在test函数中添加什么约束来告诉编译器我只需要为生命周期为'a的T实现 A<'a>呢?

完整报警如下

error: implementation of `A` is not general enough
  --> src\main.rs:29:5
   |
29 |     test(test1);
   |     ^^^^^^^^^^^ implementation of `A` is not general enough
   |
   = note: `A<'0>` would have to be implemented for the type `B<'_>`, for any lifetime `'0`...
   = note: ...but `A<'1>` is actually implemented for the type `B<'1>`, for some specific lifetime `'1`

评论区

写评论
github.com/shanliu/lsys 2024-03-21 21:07

目标把 test 下的 F, T 中的范型的生命周期参数关联上。 目前两个 for<'a> 都是独立的生命周期参数。

TinusgragLin 2024-03-21 20:38

学习了!不知道哪天会用得上(希望用不上~)

作者 yuyidegit 2024-03-21 20:34

是啊 对新的不同的WhatYouActuallyWant,都需要一个新的OkIllGiveYouWhatYouWant,而且会导致编译器无法自动推断泛型的类型

--
👇
TinusgragLin: @yuyidegit 妙啊!是不是等价于:

struct OkIllGiveYouWhatYouWant;
struct WhatYouActuallyWant<'b>(&'b i32);
trait GiveThings<'a> {
    type Output;
    fn now_take_it(a: &'a i32) -> Self::Output;
}
impl<'a> GiveThings<'a> for OkIllGiveYouWhatYouWant {
    type Output = WhatYouActuallyWant<'a>;
    fn now_take_it(a: &'a i32) -> Self::Output {
        WhatYouActuallyWant(a)
    }
}
fn test<T: for<'a> GiveThings<'a>>() {
    let input = 3;
    let out = T::now_take_it(&input);
}
test::<OkIllGiveYouWhatYouWant>();
TinusgragLin 2024-03-21 20:29

@yuyidegit 妙啊!是不是等价于:

struct OkIllGiveYouWhatYouWant;
struct WhatYouActuallyWant<'b>(&'b i32);
trait GiveThings<'a> {
    type Output;
    fn now_take_it(a: &'a i32) -> Self::Output;
}
impl<'a> GiveThings<'a> for OkIllGiveYouWhatYouWant {
    type Output = WhatYouActuallyWant<'a>;
    fn now_take_it(a: &'a i32) -> Self::Output {
        WhatYouActuallyWant(a)
    }
}
fn test<T: for<'a> GiveThings<'a>>() {
    let input = 3;
    let out = T::now_take_it(&input);
}
test::<OkIllGiveYouWhatYouWant>();
作者 yuyidegit 2024-03-21 20:15

感觉是需要这种语义

fn test<F, T>(f: F)
where
    F: Fn(T) -> i32,
    for<'a where 'a: T> T: A<'a>,
{
    let a = 1;
    f(T::from_i(&a));
}

用GAT可以实现,不过需要类型标注,对框架来说不太好用

trait A<'a>: Sized {
    type Output;
    fn from_i(a: &'a i32) -> Self::Output;
}

struct B<'a> {
    a: &'a i32,
}

impl<'a> A<'a> for B<'_> {
    type Output = B<'a>;
    fn from_i(a: &'a i32) -> Self::Output {
        B { a }
    }
}

fn test<F, T>(f: F)
where
    F: for<'a> Fn(<T as A<'a>>::Output) -> i32,
    T: for<'a> A<'a>,
{
    let a = 1;
    f(T::from_i(&a));
}

fn test1<'a>(b: B<'a>) -> i32 {
    *b.a
}

fn main() {
    test::<_, B>(test1);
}
TinusgragLin 2024-03-21 20:06

我觉得楼主想要的应该是这样的语义:

test 接受这样一个类型参数 T:

存在一个生命周期 'l 满足:'l ⊆ test 函数的生命周期,使得 T: A<'l> 成立(因为楼主想要用 test 本地的 &i32 喂给 A<'a>::from_i(&'a i32) )。

我们已经有了:

  • 对所有 'a, 都有 B<'a>: A<'a>

而我们还缺:

  • 让附着在函数上的生命周期参数可以短于函数本身生命周期的feature。
  • 表达“test 函数的生命周期”的语法。
zylthinking 2024-03-21 19:46

你说这个? for<'a> A<'a>

这个就是 A<'a>, early-bound, 只是看起来像是 later-bound 而已

这个形式我也是刚发现可以这样写, 所以 for<'a> 无脑代表 later-bound 似乎是说错了, 但说它是 early-bound 是没问题的

TinusgragLin 2024-03-21 19:26

@zylthinking,好像可以?:

trait A<'a> {
    fn letsgobaby(&self);
}
struct B<'b> {
    b: &'b i32
}
impl<'a, 'b> A<'a> for B<'b> {
    fn letsgobaby(&self) { println!("BABY LET'S DO THE {}!!!!!", self.b) }
}
fn test<T: for<'a> A<'a>>(t: T) {
    t.letsgobaby()
}
test(B { b: &69 })
zylthinking 2024-03-21 18:48

你做不到, 这种尝试是 rust 不允许的。

for<'a> 说明了 'a 是 later-bound
B<'b> 要求 'b 是 early-bound

T: for<'a> A<'a> 要求 'a 同时满足 later-bound 与 early-bound, 这是做不到的, 等同于要求一个人即是男人, 又是女人。

TinusgragLin 2024-03-21 18:31

@dakai-chen 这跟 https://github.com/rust-lang/rfcs/pull/3216 应该没什么关系?这个问题中 f 不是关键:

fn test<T>()
where
    T: for<'a> A<'a>,
{
    let input = 3;
    T::from_i(&input);
}
test::<B<'_>>(); // Same error!

我觉得问题的关键是,我们要在 test 本地用本地的 &i32、依赖 A<'a>::from_i(&'a i32) 构建 T,用本地的 &i32 就意味着不能用 fn test<'a, T: A<'a>> {...},因为 'a 要长于 test 的生命周期,但是用 T: for<'a> A<'a> 又太泛了......

dakai-chen 2024-03-21 18:00

很遗憾,这种情况我也遇到过,只能等后续 rust 更新了。

TinusgragLin 2024-03-21 17:53

我的想法是:

T: for<'a> A<'a> 这个条件在说:我的这个 T 啊,他很强,他实现了 A<'a>,而且 'a 是什么都行,是 'static 也好,是 'b, 'c, 'd 也好,他都给实现了;但是对于一个确定的 B<'a> 它只实现了 A<'a>,没有实现 A<'b>, A<'c>, A<'d>。像下面 C 这样式儿的,才能算那个超级强大的实现了所有 A<'a> 的 T:

struct C(i32);
impl<'a> A<'a> for C {
    fn from_i(a: &'a i32) -> Self {
        Self(*a)
    }
}

那咱们为了接纳 B,只能放宽要求:咱们要的这个 T,他只要实现某一个 A<'a> 就行,这个 'a 是啥咱们也不知道,但它得是“某一个”,不是“任意一个”,至于是哪一个得让编译器从上下文中推断。所以咱们的 test 函数就变成了这样式儿的:

fn test<'a, T>(f: fn (T) -> i32)
where
    T: A<'a>
{ ... }

但是这也有问题:咱们这个标在函数体上的 'a,它也不是随便哪一个生命周期都行,它最短也得长于这个函数体的执行生命周期,要不然,咱们这个函数执行到一半,欸~,'a 结束了,咱们没准还在那儿用人家此时已经被销毁了的数据。刚才说“长于”,那为啥它不能等于这个函数的执行周期呢?唉!那不就是函数本地的数据了嘛! 你看咱们 test 函数中的这一部分:

let a = 1;
..T::from_i(&a)..

这个 a,很遗憾,是个短命儿的主,函数本地变量,这函数一结束就没影儿了... 到这里我自己就只能把 test 改成这样了:

fn test<'a, T>(input: &'a i32, f: fn(T) -> i32)
where
    T: A<'a>,
{
    let out = f(T::from_i(input));
}
dakai-chen 2024-03-21 17:42

目前的 rust 还做不到,下面是我找到的有关这种情况资料。

https://github.com/tema3210/rfcs/blob/extended_hrtbs/text/3621-extended_hrtb.md

https://github.com/rust-lang/rfcs/pull/3216

1 共 13 条评论, 1 页