我该在哪里添加显式生命周期绑定?

6
编译器告诉我需要增加显式生命周期限制,但我不知道应该如何实现。
    error[E0309]: the parameter type `E` may not live long enough
      --> src/main.rs:39:9
       |
    34 | impl<S: Into<juniper::Value>, E: Into<juniper::FieldError>> Registrable for FieldInfo<S,E>
       |                               -- help: consider adding an explicit lifetime bound...: `E: 'a +`
    ...
    39 |         Box::pin(to_graphql((self.resolver)(executor)))
       |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       |
    note: ...so that the type `impl std::future::Future` will meet its required lifetime bounds
      --> src/main.rs:39:9
       |
    39 |         Box::pin(to_graphql((self.resolver)(executor)))
       |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
server_1
    error[E0309]: the parameter type `S` may not live long enough
      --> src/main.rs:39:9
       |
    34 | impl<S: Into<juniper::Value>, E: Into<juniper::FieldError>> Registrable for FieldInfo<S,E>
       |      -- help: consider adding an explicit lifetime bound...: `S: 'a +`
    ...
    39 |         Box::pin(to_graphql((self.resolver)(executor)))
       |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       |
    note: ...so that the type `impl std::future::Future` will meet its required lifetime bounds
      --> src/main.rs:39:9
       |
    39 |         Box::pin(to_graphql((self.resolver)(executor)))
       |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

这是我编写的最简代码,用于重现错误。当 ReturnType1 不泛型化 VE 时,它能够正常工作,但我现在正在尝试让其支持多种类型:
use juniper;

#[derive(Clone)]
struct Context {}

impl juniper::Context for Context {}

type ReturnType1<'a, V: Into<juniper::Value>, E: Into<juniper::FieldError>> = juniper::BoxFuture<'a, Result<V,E>>;
type InnerReturnType = juniper::ExecutionResult<juniper::DefaultScalarValue>;
type ReturnType<'a> = juniper::BoxFuture<'a, InnerReturnType>;

type Resolver<V: Into<juniper::Value>, E: Into<juniper::FieldError>> = for<'a> fn(
    &'a juniper::Executor<Context, juniper::DefaultScalarValue>
) -> ReturnType1<'a, V, E>;

async fn to_graphql<'a, V: Into<juniper::Value>, E: Into<juniper::FieldError>>(f: ReturnType1<'a, V, E>) -> InnerReturnType {
    f.await
        .map(|scalar| scalar.into())
        .map_err(|err| err.into())
}

trait Registrable: Send + Sync
{
    fn resolve<'a>(self: &Self, executor: &'a juniper::Executor<Context, juniper::DefaultScalarValue>) -> ReturnType<'a>;
}

struct FieldInfo<S, E>
where S: Into<juniper::Value>,
    E: Into<juniper::FieldError>
{
    resolver: Resolver<S,E>
}

impl<S: Into<juniper::Value>, E: Into<juniper::FieldError>> Registrable for FieldInfo<S,E>
where S: juniper::GraphQLType<TypeInfo=()> + Send + Sync
{
    fn resolve<'a>(self: &Self, executor: &'a juniper::Executor<Context, juniper::DefaultScalarValue>) -> ReturnType<'a>
    {
        Box::pin(to_graphql((self.resolver)(executor)))
    }
}

fn main() {}

Cargo.toml:

[package]
name = "pgql"
version = "0.1.0"
authors = ["Mathieu Rochette <mathieu@texthtml.net>"]
edition = "2018"

[dependencies]
juniper = { git = "https://github.com/graphql-rust/juniper", branch = "master" }

令我困扰的是泛型类型在impl块上声明,但问题似乎出现在其中的fn上,因此在impl级别添加生命周期似乎不是正确的方法。

如果我尝试在fn resolve()上添加where S: 'a, E: 'a子句:

impl<S: Into<juniper::Value>, E: Into<juniper::FieldError>> Registrable for FieldInfo<S,E>
where S: juniper::GraphQLType<TypeInfo=()> + Send + Sync
{
    fn resolve<'a>(self: &Self, executor: &'a juniper::Executor<Context, juniper::DefaultScalarValue>) -> ReturnType<'a>
    where S: 'a, E: 'a {
        Box::pin(to_graphql((self.resolver)(executor)))
    }
}

提示方法与特性声明不匹配:

error[E0195]: lifetime parameters or bounds on method `resolve` do not match the trait declaration
  --> src/main.rs:37:15
   |
24 |     fn resolve<'a>(self: &Self, executor: &'a juniper::Executor<Context, juniper::DefaultScalarValue>) -> ReturnType<'a>;
   |               ---- lifetimes in impl do not match this method in trait
...
37 |     fn resolve<'a>(self: &Self, executor: &'a juniper::Executor<Context, juniper::DefaultScalarValue>) -> ReturnType<'a>
   |               ^^^^ lifetimes do not match method in trait

但是,由于它不知道 S & E,我无法在特征上添加它......您可以在此PR中看到我试图进行的更改:https://github.com/mathroc/pgql-rs/pull/1/files

1
很难回答你的问题,因为它没有包含一个 [MRE]。我们无法确定代码中存在哪些 crates(及其版本)、types、traits、fields等。如果可能的话,在Rust Playground上尝试复现您的错误,否则在全新的Cargo项目中进行,然后[编辑]您的问题以包含额外的信息。有一些Rust特定的 MRE提示,您可以使用它们来缩小在此处发布原始代码。谢谢! - Shepmaster
具体来说,error[E0433]: failed to resolve: maybe a missing crate context? - Shepmaster
我会尝试做一个小例子。感谢@Shepmaster提供的链接。看起来在Rust playground中无法添加依赖项。我刚刚将我所拥有的推送到了github,并且我无法进行修改,这些修改在此PR中:https://github.com/mathroc/pgql-rs/pull/1。我将尝试创建一个分支,删除所有不必要的内容。 - Mathieu
我已经尽可能地减少了代码,并更新了答案 :) - Mathieu
3个回答

6
因此,在to_graphql中的await可能会在f的生命周期内的任何时间解决,当发生这种情况时,随后的into()调用显然需要相应类型中的数据有效。因此,to_graphql的类型参数VE必须比其生命周期参数'a更长。正如在async Lifetimes中所记录的那样,借用检查器会隐式地强制执行此规定:

与传统函数不同,使用引用或其他非'static参数的async fn返回一个由参数的生命周期限制的Future:

您希望指定实现块的通用类型参数SE具有这样的生命周期,但正如您所注意到的,当它未出现在特征定义中时,无法添加这样的约束。

一种可能性是向Registrable特征本身添加生命周期参数,并在resolve函数上指定这个新的特征生命周期参数是函数生命周期参数'a的子类型(即生命周期长于'a)。

trait Registrable<'b>: Send + Sync
{
    fn resolve<'a>(
        &self,
        executor: &'a juniper::Executor<Context, juniper::DefaultScalarValue>,
    ) -> ReturnType<'a>
    where 'b: 'a;
}

然后在您的impl中,您可以将该生命周期参数与泛型类型参数SE相关联:

impl<'b, S, E> Registrable<'b> for FieldInfo<S, E>
where
    S: juniper::GraphQLType<TypeInfo=()>,
    S: Send,
    S: Sync,
    S: Into<juniper::Value>,
    E: Into<juniper::FieldError>,
    S: 'b,
    E: 'b,
{
    fn resolve<'a>(
        &self,
        executor: &'a juniper::Executor<Context, juniper::DefaultScalarValue>,
    ) -> ReturnType<'a>
    where 'b: 'a
    {
        Box::pin(to_graphql((self.resolver)(executor)))
    }
}

1
这是一个有趣的问题。因为我们正在实现一个特征,所以不能在函数定义中添加额外的生命周期信息。
问题在于,S和E是泛型类型,因此可能包含引用,并因此受到这些引用的生命周期约束。但是它们是泛型的,编译器不知道它可能具有任何约束。
同时,我们返回另一个依赖于某个带有生命周期'a、S和E'的泛型类型。'a'与'S'和'E'没有任何关系,因此编译器看到并说:我无法确定它们是否足够长寿,因为它们是不相关的。
现在我还没有找到完全描述这个问题的完美方法,但是我们可以通过简单的代码更改在有限的范围内使其工作-我们可以将'S'和'E'限制为具有静态生命周期(这意味着它们不包含引用或仅包含静态引用)。在实践中,这通常已经足够了。
涉及到的代码更改在'FieldInfo'上。
struct FieldInfo<S, E>
where
    S: Into<juniper::Value> + 'static,
    E: Into<juniper::FieldError> + 'static,
{
    resolver: Resolver<S, E>,
}

我觉得应该有一种更好的方式来表达生命周期关系,但我还没有找到。

有趣,我真的不明白为什么,但它有效^^ - Mathieu

0

尝试将 where S: 'a 更改为 where S: 'a, E: 'a,以确保 E 也足够长寿。


1
如果我添加了这个,E0309 错误就消失了,但它仍然无法正常工作。我已经更新了问题以更好地解释这个问题。 - Mathieu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接