< 返回版块

Lexo-0 发表于 2021-05-01 22:34

Tags:生命周期|异步|trait|泛型

use async_trait::async_trait;
use tokio;
use core::future::Future;

async fn tester(string:&String) -> String{
    string.into()
}

static dyn_tester:&dyn App=&tester;

#[tokio::main]
async fn main() {
    let string=String::from("233");
    dyn_tester.call(&string).await;
}

#[async_trait]
trait App:Sync{
    async fn call(&self,value:&String) -> String;
}

#[async_trait]
impl <T,Fut>App for T
where 
    T:Send+Sync+Fn(&String) -> Fut,
    Fut:Send+Sync+Future<Output=String>,

{
    async fn call(&self,value:&String) -> String{
        self(value).await
    }
}

如果这样,无法通过编译。错误是 error: implementation of FnOnce is not general enough,我在https://rustcc.cn/article?id=2af9e290-bc1b-442a-ae90-39f98787675e询问过,@93996817的建议是加生命周期,于是变成这样:


use async_trait::async_trait;
use tokio;
use core::future::Future;

async fn tester(string:&String) -> String{
    string.into()
}

static dyn_tester:&dyn App=&tester;//这里看起来好像只要把static去掉,放到局部作用域就行。但实际上只是因为这只是个示例,没涉及具体逻辑。我实际使用的时候,dyn_tester的确可能是必须'static的

#[tokio::main]
async fn main() {
    let string=String::from("233");
    dyn_tester.call(&string).await;
}

#[async_trait]
trait App<'a>:Sync{
    async fn call(&self,value:&'a String) -> String;
}

#[async_trait]
impl <'a,T,Fut>App<'a> for T
where 
    T:Send+Sync+Fn(&'a String) -> Fut,
    Fut:Send+Sync+Future<Output=String>,

{
    async fn call(&self,value:&'a String) -> String{
        self(value).await
    }
}

error[E0597]: `string` does not live long enough
  --> src/main.rs:14:21
   |
14 |     dyn_tester.call(&string).await;
   |     ----------------^^^^^^^-
   |     |               |
   |     |               borrowed value does not live long enough
   |     argument requires that `string` is borrowed for `'static`
15 | }
   | - `string` dropped here while still borrowed



这次我就算不用async_trait自己手写也无法解决问题了

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=fa1e7bcd9ad966c8dadb7f9748e1956a

希望能得到各位的指导。


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

评论区

写评论
modraedlau 2021-05-02 22:17

这个问题的核心在于这句:dyn_tester.call(&string).await;,这里必须保证&'x string的生命周期必须大于或等于&'staticdyn_tester

所以第一步需要改的是传入&'static的string。

由于第一步的需要,那么说明一个问题App trait的生命周期要和其call方法中value的生命周期形成关联(value的生命周期要大于等App trait),所以第二步我们给App trait加上生命周期。

最后代码如下:

use async_trait::async_trait;
use core::future::Future;
use tokio;

async fn tester(string: &str) -> String {
    string.into()
}

static dyn_tester: &dyn App = &tester;

#[tokio::main]
async fn main() {
    let string = "233";
    dyn_tester.call(string).await;
}

#[async_trait]
trait App<'a>: Sync {
    async fn call(&'a self, value: &'a str) -> String;
}

#[async_trait]
impl<'a, T, Fut> App<'a> for T
where
    T: Send + Sync + Fn(&'a str) -> Fut,
    Fut: Send + Sync + Future<Output = String>,
{
    async fn call(&'a self, value: &'a str) -> String {
        self(value).await
    }
}
johnmave126 2021-05-02 02:30

考虑TApp实现,为了让这个call能够满足生命周期限制,我们需要

  1. 隐式的&self上的'_长于'a
  2. Self的生命周期长于'a

目前&dyn App'static的,意味着trait实现里的'a都需要延长到'static,所以本地变量的&String不够长。所以第一步要考虑把&String上的生命周期和Self的生命周期分开。一个很显然的想法是去掉App上的生命周期限制,下放到for<'a> Fn(&'a String) -> Fut

但这样会出现not general enough的报错,参考这个回答这个issue,workaround是套一个trait

use async_trait::async_trait;
use core::future::Future;
use tokio;

async fn tester(string: &String) -> String {
    string.into()
}

static dyn_tester: &dyn App = &tester;

#[tokio::main]
async fn main() {
    let string = String::from("233");
    dyn_tester.call(&string).await;
}

#[async_trait]
trait App: Sync {
    async fn call<'a>(&self, value: &'a String) -> String;
}

trait Wrapper<'a>: Send + Sync {
    type Res: Future<Output = String> + Send + Sync;
    fn wrapped_call(&self, s: &'a String) -> Self::Res;
}

impl<'a, F, Fut> Wrapper<'a> for F
where
    F: Send + Sync + Fn(&'a String) -> Fut,
    Fut: Send + Sync + Future<Output = String>,
{
    type Res = Fut;
    fn wrapped_call(&self, s: &'a String) -> Self::Res {
        self(s)
    }
}

#[async_trait]
impl<T> App for T
where
    T: for<'a> Wrapper<'a>,
{
    async fn call<'a>(&self, value: &'a String) -> String {
        self.wrapped_call(value).await
    }
}
1 共 2 条评论, 1 页