где я должен добавить явное ограничение времени жизни? - PullRequest
4 голосов
/ 06 мая 2020

Компилятор говорит мне добавить явное ограничение времени жизни, но я не могу понять, как я должен это делать.

    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 не был generi c над V и E, но теперь я пытаюсь заставить его работать с несколькими типами:

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() {}

Car go .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" }

Что меня беспокоит, так это то, что тип generi c объявлен в блоке impl, но проблема, похоже, связана с fn внутри, поэтому добавление времени жизни на уровне impl кажется неправильным способом go.

Если я попытаюсь добавить предложение where S: 'a, E: 'a в fn resolve():

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 ...

вы можете увидеть изменения Я пытаюсь сделать в этом ПР: https://github.com/mathroc/pgql-rs/pull/1/files

Ответы [ 3 ]

5 голосов
/ 10 мая 2020

Итак, await в to_graphql может разрешиться в любое время в течение жизни f - и когда это происходит, последующие вызовы into(), очевидно, требуют, чтобы данные в соответствующих типах были действительными. Параметры типа Er go, to_graphql V и E должны пережить свой параметр времени жизни 'a. Средство проверки заимствований обеспечивает это неявно, как описано в разделе async Время жизни :

В отличие от традиционных функций, async fn s, которые принимают ссылки или другие 'static аргументы, возвращают a Future, который ограничен временем жизни аргументов:

Вы хотите указать, что общие c параметры типа S и E вашего блока реализации имеют такое время жизни, но как вы отметили, вы не можете добавить такое ограничение, если оно не отображается в определении признака.

Одна из возможностей - добавить параметр времени жизни к самому признаку Registrable, а в функции resolve указать что этот новый параметр времени жизни является подтипом (т.е. переживает) параметр времени жизни функции 'a:

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

Затем на вашем impl вы можете связать этот параметр времени жизни с вашим родовым c типом параметры S и E:

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 голос
/ 09 мая 2020

Это интересный. поскольку мы реализуем черту, мы не можем добавить дополнительную информацию о времени жизни в определение функции.

Проблема в том, что S и E являются общими c типами, и, следовательно, может содержать ссылки и, следовательно, быть ограниченным временем жизни этих ссылок. Однако они являются generi c, и компилятор ничего не знает о каких-либо ограничениях, которые он может иметь.

В то же время мы возвращаем еще один generi c тип, который зависит от чего-то с временем жизни 'a и S и E. 'a совершенно не связано с S и E, поэтому компилятор смотрит и говорит: я не могу сказать, живут ли они достаточно долго, потому что они не связаны.

Теперь я не нашел идеальный способ описать это полностью, однако мы можем сделать эту работу ограниченным способом с легким изменением кода - мы можем ограничить S и E, чтобы иметь c время жизни (что означает, что они не содержат ссылок или только стати c ссылок). На практике этого обычно достаточно.

Изменение кода, о котором идет речь, FieldInfo:

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

Я считаю, что должен быть способ лучше выразить пожизненные отношения, но я еще не нашел.

0 голосов
/ 07 мая 2020

Попробуйте изменить where S: 'a на where S: 'a, E: 'a, чтобы убедиться, что E также живет достаточно долго.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...