Hi guys!
I'm stuck on a question about asynchrony in rust. I'm trying to create a simple API using Axum (the Shuttle version to deploy there) and the scraper crate.
Basically, the compiler tells me (with the help from the axum debug_handler macro) that I'm using values that are not "Send", which I can't understand as I'm awaiting for the futures. Can someone help me to understand what I'm doing wrong here?
Follow bellow the compiler messages:
error[E0277]: the trait bound `fn(axum::extract::State<MyState>) -> impl Future<Output = Result<impl IntoResponse, impl IntoResponse>> {scrape}: Handler<_, _>` is not satisfied
--> src/main.rs:153:31
|
153 | .route("/scrape", get(scrape))
| --- ^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(axum::extract::State<MyState>) -> impl Future<Output = Result<impl IntoResponse, impl IntoResponse>> {scrape}`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Handler<T, S>`:
<axum::handler::Layered<L, H, T, S> as Handler<T, S>>
<MethodRouter<S> as Handler<(), S>>
note: required by a bound in `axum::routing::get`
--> /Users/J165437/.cargo/registry/src/index.crates.io-6f17d22bba15001f/axum-0.7.5/src/routing/method_routing.rs:385:1
|
385 | top_level_handler_fn!(get, GET);
| ^^^^^^^^^^^^^^^^^^^^^^---^^^^^^
| | |
| | required by a bound in this function
| required by this bound in `get`
= note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)
error: future cannot be sent between threads safely
--> src/main.rs:66:1
|
66 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ future returned by `scrape` is not `Send`
|
= help: within `ego_tree::Node<Node>`, the trait `Sync` is not implemented for `Cell<NonZero<usize>>`, which is required by `impl Future<Output = Result<impl IntoResponse, impl IntoResponse>>: Send`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
note: future is not `Send` as this value is used across an await
--> src/main.rs:99:23
|
90 | while let (Some(name_element), Some(date_element), Some(children_element)) =
| ------------ has type `ElementRef<'_>` which is not `Send`
...
99 | match defunto.await {
| ^^^^^ await occurs here, with `name_element` maybe used later
note: required by a bound in `__axum_macros_check_scrape_future::check`
--> src/main.rs:66:1
|
66 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ required by this bound in `check`
= note: this error originates in the attribute macro `debug_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
error: future cannot be sent between threads safely
--> src/main.rs:66:1
|
66 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ future returned by `scrape` is not `Send`
|
= help: within `ego_tree::Node<Node>`, the trait `Sync` is not implemented for `UnsafeCell<tendril::tendril::Buffer>`, which is required by `impl Future<Output = Result<impl IntoResponse, impl IntoResponse>>: Send`
note: future is not `Send` as this value is used across an await
--> src/main.rs:99:23
|
90 | while let (Some(name_element), Some(date_element), Some(children_element)) =
| ------------ has type `ElementRef<'_>` which is not `Send`
...
99 | match defunto.await {
| ^^^^^ await occurs here, with `name_element` maybe used later
note: required by a bound in `__axum_macros_check_scrape_future::check`
--> src/main.rs:66:1
|
66 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ required by this bound in `check`
= note: this error originates in the attribute macro `debug_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
error: future cannot be sent between threads safely
--> src/main.rs:66:1
|
66 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ future returned by `scrape` is not `Send`
|
= help: within `ego_tree::Node<Node>`, the trait `Sync` is not implemented for `UnsafeCell<std::option::Option<std::option::Option<tendril::tendril::Tendril<tendril::fmt::UTF8>>>>`, which is required by `impl Future<Output = Result<impl IntoResponse, impl IntoResponse>>: Send`
note: future is not `Send` as this value is used across an await
--> src/main.rs:99:23
|
90 | while let (Some(name_element), Some(date_element), Some(children_element)) =
| ------------ has type `ElementRef<'_>` which is not `Send`
...
99 | match defunto.await {
| ^^^^^ await occurs here, with `name_element` maybe used later
note: required by a bound in `__axum_macros_check_scrape_future::check`
--> src/main.rs:66:1
|
66 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ required by this bound in `check`
= note: this error originates in the attribute macro `debug_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
error: future cannot be sent between threads safely
--> src/main.rs:66:1
|
66 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ future returned by `scrape` is not `Send`
|
= help: within `ego_tree::Node<Node>`, the trait `Sync` is not implemented for `UnsafeCell<std::option::Option<Vec<string_cache::atom::Atom<markup5ever::LocalNameStaticSet>>>>`, which is required by `impl Future<Output = Result<impl IntoResponse, impl IntoResponse>>: Send`
note: future is not `Send` as this value is used across an await
--> src/main.rs:99:23
|
90 | while let (Some(name_element), Some(date_element), Some(children_element)) =
| ------------ has type `ElementRef<'_>` which is not `Send`
...
99 | match defunto.await {
| ^^^^^ await occurs here, with `name_element` maybe used later
note: required by a bound in `__axum_macros_check_scrape_future::check`
--> src/main.rs:66:1
|
66 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ required by this bound in `check`
= note: this error originates in the attribute macro `debug_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
error: future cannot be sent between threads safely
--> src/main.rs:66:1
|
66 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ future returned by `scrape` is not `Send`
|
= help: within `ego_tree::Node<Node>`, the trait `Sync` is not implemented for `*mut tendril::fmt::UTF8`, which is required by `impl Future<Output = Result<impl IntoResponse, impl IntoResponse>>: Send`
note: future is not `Send` as this value is used across an await
--> src/main.rs:99:23
|
90 | while let (Some(name_element), Some(date_element), Some(children_element)) =
| ------------ has type `ElementRef<'_>` which is not `Send`
...
99 | match defunto.await {
| ^^^^^ await occurs here, with `name_element` maybe used later
note: required by a bound in `__axum_macros_check_scrape_future::check`
--> src/main.rs:66:1
|
66 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ required by this bound in `check`
= note: this error originates in the attribute macro `debug_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
error: future cannot be sent between threads safely
--> src/main.rs:66:1
|
66 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ future returned by `scrape` is not `Send`
|
= help: within `ego_tree::Node<Node>`, the trait `Sync` is not implemented for `Cell<usize>`, which is required by `impl Future<Output = Result<impl IntoResponse, impl IntoResponse>>: Send`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicUsize` instead
note: future is not `Send` as this value is used across an await
--> src/main.rs:99:23
|
90 | while let (Some(name_element), Some(date_element), Some(children_element)) =
| ------------ has type `ElementRef<'_>` which is not `Send`
...
99 | match defunto.await {
| ^^^^^ await occurs here, with `name_element` maybe used later
note: required by a bound in `__axum_macros_check_scrape_future::check`
--> src/main.rs:66:1
|
66 | #[debug_handler]
| ^^^^^^^^^^^^^^^^ required by this bound in `check`
= note: this error originates in the attribute macro `debug_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0277`.
error: could not compile `clovis-bevilacqua` (bin "clovis-bevilacqua") due to 7 previous errors
And my full source code:
use std::borrow::Borrow;
use axum::{
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
routing::{get, post},
Json, Router,
};
use axum_macros::debug_handler;
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, PgPool, Pool, Postgres};
use reqwest;
use scraper::{Html, Selector};
use tokio::task;
async fn get_falecido_by_nome_data(
nome: String,
date: String,
pool: &Pool<Postgres>,
) -> Result<Falecido, sqlx::Error> {
match sqlx::query_as::<_, Falecido>("SELECT id, nome, data, filhos FROM falecidos WHERE nome = $1 AND data = $2")
.bind(nome)
.bind(date)
.fetch_one(pool)
.await
{
Ok(defunto) => Ok(defunto),
Err(e) => Err(e),
}
}
async fn retrieve(
Path(nome): Path<String>,
Path(date): Path<String>,
State(state): State<MyState>,
) -> Result<impl IntoResponse, impl IntoResponse> {
match sqlx::query_as::<_, Falecido>("SELECT id FROM falecidos WHERE nome = $1 AND data = $2")
.bind(nome)
.bind(date)
.fetch_one(&state.pool)
.await
{
Ok(todo) => Ok((StatusCode::OK, Json(todo))),
Err(e) => Err((StatusCode::BAD_REQUEST, e.to_string())),
}
}
async fn add(
State(state): State<MyState>,
Json(data): Json<FalecidoNew>,
) -> Result<impl IntoResponse, impl IntoResponse> {
match sqlx::query_as::<_, Falecido>("INSERT INTO falecidos (nome, data, filhos) VALUES ($1, $2, $3) RETURNING id, nome, data, filhos")
.bind(&data.nome)
.bind(&data.data)
.bind(&data.filhos)
.fetch_one(&state.pool)
.await
{
Ok(todo) => Ok((StatusCode::CREATED, Json(todo))),
Err(e) => Err((StatusCode::BAD_REQUEST, e.to_string())),
}
}
#[debug_handler]
async fn scrape(
State(state): State<MyState>,
) -> Result<impl IntoResponse, impl IntoResponse> {
let url = "https://setec.sp.gov.br/falecimentos.html";
let body = task::spawn_blocking(move || {
reqwest::blocking::get(url).unwrap().text().unwrap()
}).await.unwrap();
let document = Html::parse_document(&body);
// Ajuste os seletores conforme necessário
let name_selector = Selector::parse("div.falecimento-nome").unwrap();
let date_selector = Selector::parse("div.falecimento-data").unwrap();
let children_selector = Selector::parse("div.falecimento-filhos").unwrap();
let mut names = document.select(&name_selector);
let mut dates = document.select(&date_selector);
let mut children = document.select(&children_selector);
let mut falecidos_adicionados = 0;
let mut falecidos: Vec<Falecido> = Vec::new();
while let (Some(name_element), Some(date_element), Some(children_element)) =
(names.next(), dates.next(), children.next())
{
let nome = name_element.text().collect::<Vec<_>>().join(" ").clone();
let data = date_element.text().collect::<Vec<_>>().join(" ").clone();
let filhos = children_element.text().collect::<Vec<_>>().join(" ").clone();
// Verifica se já existe um registro com o mesmo nome e data
let defunto = get_falecido_by_nome_data(nome.to_string(), data.to_string(), state.pool.borrow());
match defunto.await {
Ok(_) => {
println!("Falecido {nome} já cadastrado");
}
Err(_) => {
// Insere o falecido no banco de dados
match sqlx::query_as::<_, Falecido>("INSERT INTO falecidos (nome, data, filhos) VALUES ($1, $2, $3) RETURNING id, nome, data, filhos")
.bind(nome.to_string())
.bind(data.to_string())
.bind(filhos.to_string())
.fetch_one(&state.pool)
.await
{
Ok(defunto) => {
falecidos.push(defunto);
falecidos_adicionados += 1;
},
Err(e) => {
println!("Erro ao adicionar falecido {0}", e.to_string());
},
}
}
}
}
if falecidos_adicionados > 0{
Ok((StatusCode::CREATED, Json(falecidos)))
}
else if falecidos_adicionados == 0 {
Ok((StatusCode::NO_CONTENT, Json(falecidos)))
}
else{
let response = format!("Erro ao adicionar falecidos");
Err((StatusCode::BAD_REQUEST, response.to_string()))
}
}
#[derive(Clone)]
struct MyState {
pool: PgPool,
}
#[shuttle_runtime::main]
async fn main(#[shuttle_shared_db::Postgres] pool: PgPool) -> shuttle_axum::ShuttleAxum {
sqlx::migrate!()
.run(&pool)
.await
.expect("Failed to run migrations");
let state = MyState { pool };
let router = Router::new()
.route("/falecidos", post(add))
.route("/falecidos/:id", get(retrieve))
.route("/scrape", get(scrape))
.with_state(state);
Ok(router.into())
}
#[derive(Deserialize)]
struct FalecidoNew {
pub nome: String,
pub data: String,
pub filhos: String,
}
#[derive(Serialize, FromRow)]
struct Falecido {
pub id: i32,
pub nome: String,
pub data: String,
pub filhos: String,
}
I appreciate any information that helps me solve this problem.