Проблемы с настройкой фильтров деформации в Rust

Я пытаюсь настроить простой GET фильтр, но не могу его скомпилировать.

Это функция, которую я пытаюсь сопоставить с запросом:

pub async fn get_users(reference_counter: Arc<RwLock<MysqlConnection>>) -> Result<impl Reply, Rejection> {
   // Get users code here
}

Вот как я сопоставляю функцию с запросом GET в main.rs:

#[tokio::main]
async fn main() {
   let db_connection = storage::establish_connection();
   let lock = RwLock::new(db_connection);
   let reference_counter = Arc::new(lock);
   let ref_filter = warp::any().map(move || reference_counter.clone());
   let get_users = warp::get()
           .and(warp::path("users"))
           .and(ref_filter.clone())
           .and_then(user::get_users);
   warp::serve(get_users).run(([127, 0, 0, 1], 3030)).await;
}

Ошибка компиляции происходит на and_then, это довольно загадочно, и вот что написано:

error[E0599]: no method named `and_then` found for struct `warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>` in the current scope
  --> src\main.rs:21:14
   |
21 |             .and_then(user::get_users);
   |              ^^^^^^^^ method not found in `warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>`
   |
  ::: C:\Users\Yasmani\.cargo\registry\src\github.com-1ecc6299db9ec823\warp-0.2.3\src\filter\and.rs:12:1
   |
12 | pub struct And<T, U> {
   | --------------------
   | |
   | doesn't satisfy `_: warp::filter::FilterBase`
   | doesn't satisfy `_: warp::filter::Filter`
   |
   = note: the method `and_then` exists but the following trait bounds were not satisfied:
           `warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>: warp::filter::FilterBase`
           which is required by `warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>: warp::filter::Filter`
           `&warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>: warp::filter::FilterBase`
           which is required by `&warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>: warp::filter::Filter`
           `&mut warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>: warp::filter::FilterBase`
           which is required by `&mut warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>: warp::filter::Filter`

Я считаю, что это как-то связано с типом, возвращаемым замыканием в ref_filter, не соответствующим типу, ожидаемому функцией get_users, но я не уверен, почему. Я считаю, что замыкание возвращает Arc<RwLock<MysqlConnection>>>, а функция get_users принимает то же самое. В чем проблема?


person AxiomaticNexus    schedule 18.06.2020    source источник


Ответы (2)


Проблема в том, что тип возврата вашего закрытия по какой-то причине неизвестен. Если внимательно присмотреться к ошибке компилятора, возвращаемый тип вашего закрытия - _. Это приводит к тому, что тип возвращаемого значения следующей части вашего фильтра GET не будет Filter, что, в свою очередь, означает, что and_then() не реализован.

let get_users = warp::get()
        .and(warp::path("users"))
        .and(ref_filter.clone())

Решение

Аннотируйте возвращаемый тип вашего закрытия.

let ref_filter = warp::any().map(move || -> Arc<RwLock<MysqlConnection>> {reference_counter.clone()});

(Возможно, вы обнаружите, что тип db_connection не такой, как вы ожидали.)

Контекст

Мне кажется, что тип db_connection действительно является корнем вашей проблемы. Я попытался создать MWE из вашего кода, заменив пустую структуру или impl Foo на db_connection, но он скомпилировался без каких-либо проблем.

person Emerson Harkin    schedule 18.06.2020
comment
Я попробовал ваш эксперимент с пустым типом struct и получил тот же результат; он компилируется отлично. Однако, если я добавлю поле типа MysqlConnection в struct, все снова сломается. Похоже, что всякий раз, когда вы внедряете MysqlConnection куда угодно, это раздражает API-интерфейсы Warp. Почему? MysqlConnection - это просто тип, который я получаю от API-интерфейсов Diesel. Кажется, что struct неявно извлекает что-то (Copy, Clone или что-то в этом роде), когда он пуст, а затем прекращает извлечение, когда я добавляю соединение как поле. Любые идеи? - person AxiomaticNexus; 19.06.2020
comment
Я понял. Это как-то связано с MysqlConnection невыполнением некоторых trait требований Warp. Точный trait я не знаю, но я нашел другой подход. Я отправлю полный ответ о том, как это сделать. - person AxiomaticNexus; 19.06.2020
comment
Рад, что ты понял это @AxiomaticNexus! - person Emerson Harkin; 19.06.2020

Проблема в том, что MysqlConnection из API-интерфейсов Diesel не реализует некоторые trait, которые требуются API-интерфейсам Warp. Решение состоит в том, чтобы перестать использовать тип MysqlConnection прямо здесь и вместо этого использовать ConnectionPool, который генерирует соединения на лету. ConnectionPool является частью функции r2d2 пакета Diesel.

Теперь, когда я думаю об этом, это имеет смысл. Раньше я использовал одно соединение с базой данных для обработки всех входящих HTTP-запросов. Это плохо, потому что приложение может в конечном итоге выполнить несколько операций чтения в одном и том же соединении одновременно.

Итак, вот функция, обрабатывающая HTTP-запрос:

type ConnectionPool = Pool<ConnectionManager<MysqlConnection>>;
type ConnectionLock = RwLock<ConnectionPool>;
type ConnectionLockArc = Arc<ConnectionLock>;

pub async fn get_users(lock: ConnectionLockArc) -> Result<impl Reply, Rejection>{
    let users = storage::get_users(&lock);
    let response = json(&users);
    return Ok(response);
}

А вот как это настраивается с помощью API-интерфейсов Warp:

#[tokio::main]
async fn main() {
     let pool = storage::establish_connection_pool();
     let lock = RwLock::new(pool);
     let reference = Arc::new(lock);
     let resources = warp::any().map(move || reference.clone());
     let get_users = warp::get()
             .and(warp::path("users"))
             .and(resources.clone())
             .and_then(user::get_users);
     warp::serve(get_users).run(([127, 0, 0, 1], 3030)).await;
 }
person AxiomaticNexus    schedule 19.06.2020
comment
Почему вы используете RwLock и Arc? - person maxcountryman; 14.07.2020
comment
@maxcountryman Приложение по своей сути многопоточное. RwLock предотвращает выполнение одним потоком операции чтения в базе данных, в то время как другой выполняет запись в нее. Arc позволяет использовать RwLock в нескольких потоках. - person AxiomaticNexus; 14.07.2020