Повторное использование перемещенной переменной для фильтров деформации

Я хочу использовать Rust и Juniper для создания сервера GraphQL. Этот сервер должен получить доступ к базе данных.

Я пытался выполнить этот пример кода от Juniper, но он использует пустой Context для передачи Schema; Мне нужно отправить пул для подключения к базе данных.

Я хочу иметь возможность подключаться к GraphQL через POST, GET и веб-сокеты.

type RepositoryPool = r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;

fn graphql(
    db_pool: RepositoryPool,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
    let state = warp::any().map(move || Context::new(db_pool.clone()));
    let root_node = Arc::new(schema());
    let graphql_filter = make_graphql_filter(schema(), state.boxed());

    let post_filter = warp::post()
        .and(warp::body::content_length_limit(1024 * 16))
        .and(graphql_filter.clone());

    let get_filter = warp::get().and(graphql_filter);

    let ws_filter = warp::ws().map(move |ws: warp::ws::Ws| {
        let root_node = root_node.clone();
        let db_pool = db_pool.clone();
        ws.on_upgrade(move |websocket| async move {
            serve_graphql_ws(
                websocket,
                root_node,
                ConnectionConfig::new(Context::new(db_pool)),
            )
            .map(|r| {
                if let Err(e) = r {
                    println!("Websocket error: {}", e);
                }
            })
            .await
        })
    });

    warp::path("graphql").and(get_filter.or(post_filter).or(ws_filter))
}

Однако я получаю сообщение об ошибке:

error[E0382]: use of moved value: `db_pool`
  --> src\filters.rs:39:34
   |
27 |   db_pool: RepositoryPool,
   |   ------- move occurs because `db_pool` has type `r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>`, which does not implement the `Copy` trait
28 | ) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
29 |   let state = warp::any().map(move || Context::new(db_pool.clone()));
   |                               -------              ------- variable moved due to use in closure
   |                               |
   |                               value moved into closure here
...
39 |   let ws_filter = warp::ws().map(move |ws: warp::ws::Ws| {
   |                                  ^^^^^^^^^^^^^^^^^^^^^^^ value used here after move
40 |     let root_node = root_node.clone();
41 |     let db_pool = db_pool.clone();
   |                   ------- use occurs due to use in closure

Я недостаточно понимаю, как эти значения moved решают эту проблему. Как решить подобные проблемы?


person Lanbo    schedule 26.10.2020    source источник
comment
Похоже, вам нужно клонировать пул за пределами закрытия, а затем только перемещать клон.   -  person Sven Marnach    schedule 26.10.2020
comment
@SvenMarnach: Синтаксис для этого немного громоздкий, но для этого есть ящик!: заключить.   -  person rodrigo    schedule 26.10.2020
comment
@SvenMarnach, к сожалению, я пытался засорить этот код db_pool.clone() практически где угодно и не могу понять, где его клонировать.   -  person Lanbo    schedule 26.10.2020
comment
let db_pool_clone = db_pool.clone();, а затем переместить db_pool_clone в замыкание?   -  person Cerberus    schedule 26.10.2020
comment
Трудно ответить на ваш вопрос, потому что он не включает минимально воспроизводимый пример. Мы не можем сказать, какие крейты (и их версии), типы, трейты, поля и т. д. присутствуют в коде. Нам будет легче помочь вам, если вы попытаетесь воспроизвести ошибку на Rust Playground, если возможно, в противном случае в новом проекте Cargo отредактируйте свой вопрос, чтобы включить дополнительную информацию. Существуют советы по MRE для Rust, которые вы можете использовать, чтобы уменьшить исходный код для публикации здесь. Спасибо!   -  person Shepmaster    schedule 26.10.2020


Ответы (1)


Благодаря некоторым комментариям я выяснил способ, который принимает компилятор Rust:

fn graphql(
    db_pool: RepositoryPool,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
    let db_pool_clone = db_pool.clone();
    let state = warp::any().map(move || Context::new(db_pool_clone.clone()));
    let root_node = Arc::new(schema());
    let graphql_filter = make_graphql_filter(schema(), state.boxed());

    let post_filter = warp::post()
        .and(warp::body::content_length_limit(1024 * 16))
        .and(graphql_filter.clone());

    let get_filter = warp::get().and(graphql_filter);

    let ws_filter = warp::ws().map(move |ws: warp::ws::Ws| {
        let root_node = root_node.clone();
        let db_pool = db_pool.clone();
        ws.on_upgrade(move |websocket| async move {
            serve_graphql_ws(
                websocket,
                root_node,
                ConnectionConfig::new(Context::new(db_pool)),
            )
            .map(|r| {
                if let Err(e) = r {
                    println!("Websocket error: {}", e);
                }
            })
            .await
        })
    });

    warp::path("graphql").and(get_filter.or(post_filter).or(ws_filter))
}

Я не совсем понимаю, зачем это нужно, но пока работает.

person Lanbo    schedule 26.10.2020
comment
это необходимо, потому что замыкание move перемещает все указанные имена внутри замыкания, поэтому, когда замыкание использует ` db_pool.clone(), Rust immediately moves db_pool` в замыкание при создании; и только клоны сказали db_pool, когда закрытие выполняется. - person Masklinn; 27.10.2020
comment
Между прочим, распространенным шаблоном для такого рода ситуаций является шаблон пункта точного захвата: /" rel="nofollow noreferrer">smallcultfollowing.com/babysteps/blog/2018/04/24/ - person Masklinn; 27.10.2020