代码之家  ›  专栏  ›  技术社区  ›  Aleksandar Dimitrov

有没有办法延长局部定义变量的生存期?

  •  0
  • Aleksandar Dimitrov  · 技术社区  · 5 年前

    用例:我有一个分页的Graphql API,其中许多不同的实体返回一个不透明光标和一个布尔值 hasNext .我想将这些实体作为 TryStream 允许在提取所有页面时进行计算。

    我定义了一个特征来抽象它。 get_data 获取单个页面:

    trait PaginatedEntityQuery {
        type ResponseData;
        fn get_cursor(data: &Self::ResponseData) -> Option<String>;
        fn has_next_page(data: &Self::ResponseData) -> bool;
        fn get_data<'a>(
            &self, // access to query variables
            backend: &'a Backend,  // access to a backend to get data from
            cursor: Option<String>,  // the cursor in the pagination
        ) -> Pin<Box<dyn Future<Output = Result<Self::ResponseData>> + 'a>>;
    }
    

    我在用 try_unfold 在我的后台 impl :

    fn get_paginated_entities<'a, T>(
      &'a self,
      query: &'a TryStream<Ok = T::ResponseData, Error = anyhow::Error> + 'a
    where
      T: PaginatedEntity
    {
      try_unfold(StreamState::Start, async move |state| {
        // stream state handling code that defines `cursor`
        let data = query.get_data(self, cursor).await?
        // more data + state computations + return result of `get_data`.
      }
    }
    

    现在我想定义一下

    struct SomeEntityQuery { filter: String }
    impl PaginatedEntityQuery for SomeEntityQuery { /* … */ }
    

    最后,我可以定义一个使用 get_paginated_entities 要做举重:

    fn get_some_entities(filter: String, /* … */) -> impl TryStream<Ok = Vec<SomeOtherType>, Error = anyhow::Error> + 'a {
      backend.get_paginated_entities(&SomeEntityQuery { filter }).map_ok /* … */
    }
    

    当然,这是行不通的。我定义了一个 SomeEntityQuery 在里面 get_some_entities 但我要回归自己的价值观。铁锈不可能知道 sometentityquery 实际显示在返回值中。

    我会 query: &'a T 在里面 找到一些实体 拥有而不是裁判,但之后我搬家了 t 在外面 async move 我给的 试试看 既然这是一个 FnMut 不管怎么说,那封信不能多次拥有它。是的 query 必须被分享。另外,删除ref表单 T 获取Rust以抱怨类型参数可能没有足够长的寿命。

    有没有办法确保查询本身的值不需要与查询返回的值一样长?或者可能是为了延长查询的生命周期?如果需要,我可以大量复制查询。它重量轻,性能受网络限制。

    0 回复  |  直到 5 年前
        1
  •  3
  •   dominicm00    5 年前

    首先让我们做一个简单的工作示例,我们可以实际编译:

    trait Query {
        fn get_data<'backend>(&self, backend: &'backend Backend) -> Result<'backend>;
    }
    
    struct Result<'data> {
        data: &'data str,
    }
    
    struct Backend {
        database_data: String,
    }
    
    struct SomeQuery {
        length_filter: usize,
    }
    
    impl Query for SomeQuery {
        fn get_data<'backend>(&self, backend: &'backend Backend) -> Result<'backend> {
            Result {
                // we use SomeQuery here, but it's not in the data
                data: &backend.database_data[..self.length_filter],
            }
        }
    }
    
    impl Backend {
        fn get_paginated_entities<'backend>(
            &'backend self,
            query: &'backend SomeQuery,
        ) -> Result<'backend> {
            query.get_data(self)
        }
    }
    
    fn get_some_entities<'backend>(
        length_filter: usize,
        backend: &'backend Backend,
    ) -> Result<'backend> {
        backend.get_paginated_entities(&SomeQuery { length_filter })
    }
    

    希望现在更容易找到问题;Rust认为数据与查询具有相同的生存期,因为我们 告诉 是的!

    fn get_paginated_entities<'backend>(
        // the returned data is referenced from the backend,
        // so we specify a lifetime that Result lives as long
        // as backend does
        &'backend self,
    
        // but we told Rust here that query also has a lifetime
        // of 'backend, so the query also has to live as long as
        // the return value does
        query: &'backend SomeQuery,
    ) -> Result<'backend> {
        query.get_data(self)
    }
    

    在这里,我们可以使用多个生存期来表示返回值的生存期与查询生存期没有任何关系(在本例中,我们实际上不需要它,但您可以在代码中使用它)。

    fn get_paginated_entities<'backend, 'query>(
        &'backend self,
        // now the return data isn't linked to query's lifetime
        query: &'query SomeQuery,
    ) -> Result<'backend> {
        query.get_data(self)
    }
    

    这很好,因为 get_data 还规定 query 的寿命与 Result .

    fn get_data<'backend>(&self, backend: &'backend Backend) -> Result<'backend>;
    

    编辑:这里的问题也与 async ; 因为我们不知道代码何时运行,所以数据必须是 'static .我们可以用 Rc 而不是普通的参考。 钢筋混凝土 实际上,它有一个静态的生命周期,因为它可以永远浮动,直到不再有对它的引用。

    推荐文章