< 返回版块

wukong 发表于 2023-03-19 11:29

Tags: lifetime, async

请求,下面这段代码中, impl<'r, A> FromText<'r> for (A,) 能编译通过, impl<'r, A, B> FromText<'r> for (A, B) 却不能编译通过。

应该如何理解这段代码的错误?


Ext Link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=bfc9d867e1a7c13b3b4812ee7475eab7

评论区

写评论
作者 wukong 2023-03-20 15:52

这样更能表达意图 👍

--
👇
苦瓜小仔: 你可以使用 GAT:playground

jackalchenxu 2023-03-20 12:43

多谢,又学到一点新知识。

--
👇
苦瓜小仔: 你可以使用 GAT:playground

苦瓜小仔 2023-03-19 22:36

你可以使用 GAT:playground

作者 wukong 2023-03-19 21:09

@jackalchenxu 谢谢你的回复,不过我这里需要为一些引用实现 FromText, 比如:

impl <'r> FromText<'r> for &str {
  type Output = &'r str;
  async fn parse(text: &'r Text) -> Self::Output  {
     // get string ref from text
  }
}

而如果使用你建议的方法的话,我就很那做到上面这个实现。

--
👇
jackalchenxu: 不使用您代码中的 'r 生命周期声明,而使用特别的 'async_trait

这里是可以编译过的修改版本

作者 wukong 2023-03-19 15:24

是的,我也发现与跨 .await 有关,比如这个代码

--
👇
苦瓜小仔: 还可以把 'r 放到函数上

至于 Send 问题,那是因为跨 .await,注意我的链接并没有 B::Output: Send

any variables used in async bodies must be able to travel between threads, as any .await can potentially result in a switch to a new thread

src: .awaiting on a Multithreaded Executor

而这里的生命周期问题,也与跨 .await 有关(貌似目前这方面有点混乱),见 https://github.com/rust-lang/rust/issues/102211

苦瓜小仔 2023-03-19 15:17

我已经将这里的例子提交到了那个 issue。

苦瓜小仔 2023-03-19 14:56

还可以把 'r 放到函数上

至于 Send 问题,那是因为跨 .await,注意我的链接并没有 B::Output: Send

any variables used in async bodies must be able to travel between threads, as any .await can potentially result in a switch to a new thread

src: .awaiting on a Multithreaded Executor

而这里的生命周期问题,也与跨 .await 有关(貌似目前这方面有点混乱),见 https://github.com/rust-lang/rust/issues/102211

jackalchenxu 2023-03-19 14:36

不使用您代码中的 'r 生命周期声明,而使用特别的 'async_trait

这里是可以编译过的修改版本

作者 wukong 2023-03-19 13:06

@Jasonwu123 我有一个办法证明不是 Send 的问题 ,见这个 playground

上面的playground什么都不变,只是不调用 B::parse(text).await(见37行),并用todo!() 帮忙通过编译, 也不用增加 Self::Output的约束,结果也可以编译通过。因此应该不是 Send 的问题。

--
👇
wukong: 这样会有递归的报错,见 playground 的错误

同时我也有疑问,A 和 B 都是 Send 后, (A, B) 不就是 Send 了吗?

另外,我不使用 async_trait 这个库,而使用 AFIT 和 impl_trait_projections 这两个 nightly 特性时就不会有问题,见 playground

--
👇
Jasonwu123: 这个问题是由于Rust编译器在编译时,对于async fn的返回类型需要满足Send约束。这是因为异步操作可能在不同的线程之间传递。在第一个实现中,代码可以正常编译,因为单元素元组(A::Output,)默认满足Send。但在第二个实现中,需要明确指出A::OutputB::Output满足Send

你已经在where子句中添加了A::Output: SendB::Output: Send。但是,async_trait库要求Output类型也必须满足Send。在实现(A, B)的代码中,应该将type Output改为:

rustCopy code
type Output = (A::Output, B::Output);

同时,需要在where子句中添加Output类型满足Send的约束:

rustCopy codewhere
    A: FromText<'r>,
    B: FromText<'r>,
    A::Output: Send,
    B::Output: Send,
    Self::Output: Send,

完整的实现应如下:

rustCopy code#[async_trait]
impl<'r, A, B> FromText<'r> for (A, B)
where
    A: FromText<'r>,
    B: FromText<'r>,
    A::Output: Send,
    B::Output: Send,
    Self::Output: Send,
{
    type Output = (A::Output, B::Output);

    async fn parse(text: &'r Text) -> Self::Output {
        let a = A::parse(text).await;
        let b = B::parse(text).await;
        (a, b)
    }
}

这样修改后,代码应该可以编译通过。

作者 wukong 2023-03-19 12:42

这样会有递归的报错,见 playground 的错误

同时我也有疑问,A 和 B 都是 Send 后, (A, B) 不就是 Send 了吗?

另外,我不使用 async_trait 这个库,而使用 AFIT 和 impl_trait_projections 这两个 nightly 特性时就不会有问题,见 playground

--
👇
Jasonwu123: 这个问题是由于Rust编译器在编译时,对于async fn的返回类型需要满足Send约束。这是因为异步操作可能在不同的线程之间传递。在第一个实现中,代码可以正常编译,因为单元素元组(A::Output,)默认满足Send。但在第二个实现中,需要明确指出A::OutputB::Output满足Send

你已经在where子句中添加了A::Output: SendB::Output: Send。但是,async_trait库要求Output类型也必须满足Send。在实现(A, B)的代码中,应该将type Output改为:

rustCopy code
type Output = (A::Output, B::Output);

同时,需要在where子句中添加Output类型满足Send的约束:

rustCopy codewhere
    A: FromText<'r>,
    B: FromText<'r>,
    A::Output: Send,
    B::Output: Send,
    Self::Output: Send,

完整的实现应如下:

rustCopy code#[async_trait]
impl<'r, A, B> FromText<'r> for (A, B)
where
    A: FromText<'r>,
    B: FromText<'r>,
    A::Output: Send,
    B::Output: Send,
    Self::Output: Send,
{
    type Output = (A::Output, B::Output);

    async fn parse(text: &'r Text) -> Self::Output {
        let a = A::parse(text).await;
        let b = B::parse(text).await;
        (a, b)
    }
}

这样修改后,代码应该可以编译通过。

Jasonwu123 2023-03-19 11:48

这个问题是由于Rust编译器在编译时,对于async fn的返回类型需要满足Send约束。这是因为异步操作可能在不同的线程之间传递。在第一个实现中,代码可以正常编译,因为单元素元组(A::Output,)默认满足Send。但在第二个实现中,需要明确指出A::OutputB::Output满足Send

你已经在where子句中添加了A::Output: SendB::Output: Send。但是,async_trait库要求Output类型也必须满足Send。在实现(A, B)的代码中,应该将type Output改为:

rustCopy code
type Output = (A::Output, B::Output);

同时,需要在where子句中添加Output类型满足Send的约束:

rustCopy codewhere
    A: FromText<'r>,
    B: FromText<'r>,
    A::Output: Send,
    B::Output: Send,
    Self::Output: Send,

完整的实现应如下:

rustCopy code#[async_trait]
impl<'r, A, B> FromText<'r> for (A, B)
where
    A: FromText<'r>,
    B: FromText<'r>,
    A::Output: Send,
    B::Output: Send,
    Self::Output: Send,
{
    type Output = (A::Output, B::Output);

    async fn parse(text: &'r Text) -> Self::Output {
        let a = A::parse(text).await;
        let b = B::parse(text).await;
        (a, b)
    }
}

这样修改后,代码应该可以编译通过。

1 共 11 条评论, 1 页