Unexplained trait bound no longer satisfied when modifying Axum handler body

huangapple go评论67阅读模式
英文:

Unexplained trait bound no longer satisfied when modifying Axum handler body

问题

以下是您提供的Rust代码的翻译:

最近,我开始学习Rust,以使用Axum构建一些简单的Web应用程序。但是,在构建应用程序时出现了一个无法解释的错误,我无法找到其原因。

无法构建的应用程序代码如下所示:

```rust
use axum::{
    body::{Body, boxed, StreamBody},
    Extension,
    http::Request,
    response::Response,
    Router,
    routing::get,
};
use std::{
    path::PathBuf,
    sync::{Arc, RwLock},
};

type Cache = Arc<RwLock<PathBuf>>;

async fn handle_proxy(
    cache: Extension<Cache>,
    req: Request<Body>,
) -> Response {
    let cache = cache.read().unwrap();

    if let Some(object_key) = req.uri().path().rsplitn(2, '/').next() {
        // 后续将使用cache变量构建给定的路径
        let file = tokio::fs::File::open("/tmp/testaroo").await;
        let buf_reader = tokio::io::BufReader::new(file.unwrap());
        let stream = tokio_util::io::ReaderStream::new(buf_reader);
        return Response::builder().body(boxed(StreamBody::new(stream))).unwrap();
    }

    // 此处未完全构建该函数,稍后将添加更多代码,不必演示错误。

    Response::builder().body(boxed("".to_string())).unwrap()
}

#[tokio::main]
async fn main() {
    let app = Router::new().route("/:key", get(handle_proxy));

    let addr = "0.0.0.0:8080";
    axum::Server::bind(&addr.parse().unwrap()).serve(app.into_make_service()).await.unwrap();
}

关于您提到的错误,问题似乎出现在handle_proxy函数的处理上,特别是这一行:let cache = cache.read().unwrap();。这个错误表明在get(handle_proxy)中,Rust 编译器无法确定handle_proxy函数是否实现了axum::handler::Handler trait。

要解决此问题,您可以尝试为handle_proxy函数显式指定其返回类型,以确保它满足axum::handler::Handler trait 的要求。例如:

async fn handle_proxy(
    cache: Extension<Cache>,
    req: Request<Body>,
) -> Response<Body> {
    let cache = cache.read().unwrap();

    // ...
}

这将明确告诉编译器handle_proxy函数的返回类型,以符合Axum的要求。希望这有助于解决问题。如果您需要更多帮助,请告诉我。

英文:

Recently, I've started to learn Rust to build some simple web apps with Axum. However, I ended having an unexplained error while building the app, and I could not find the reason of it.

The failing to be built application code is the following:

use axum::{
    body::{Body, boxed, StreamBody},
    Extension,
    http::Request,
    response::Response,
    Router,
    routing::get,
};
use std::{
    path::PathBuf,
    sync::{Arc, RwLock},
};

type Cache = Arc&lt;RwLock&lt;PathBuf&gt;&gt;;

async fn handle_proxy(
    cache: Extension&lt;Cache&gt;,
    req: Request&lt;Body&gt;,
) -&gt; Response {
    let cache = cache.read().unwrap();

    if let Some(object_key) = req.uri().path().rsplitn(2, &#39;/&#39;).next() {
        // later the cache variable will be used to build the given path
        let file = tokio::fs::File::open(&quot;/tmp/testaroo&quot;).await;
        let buf_reader = tokio::io::BufReader::new(file.unwrap());
        let stream = tokio_util::io::ReaderStream::new(buf_reader);
        return Response::builder().body(boxed(StreamBody::new(stream))).unwrap();
    }

    // The function is not fully built here, there will be more code later, useless to demo the error.

    Response::builder().body(boxed(&quot;&quot;.to_string())).unwrap()
}

#[tokio::main]
async fn main() {
    let app = Router::new().route(&quot;/:key&quot;, get(handle_proxy));

    let addr = &quot;0.0.0.0:8080&quot;;
    axum::Server::bind(&amp;addr.parse().unwrap()).serve(app.into_make_service()).await.unwrap();
}

When the let cache = cache.read().unwrap(); line is present (not commented above), the program is no longer building with the following error:

&gt; cargo clippy
    Checking hello-rs-axum-failure v0.1.0 (/home/mycroft/tmp/broken-thing)
error[E0277]: the trait bound `fn(axum::Extension&lt;std::sync::Arc&lt;std::sync::RwLock&lt;std::path::PathBuf&gt;&gt;&gt;, axum::http::Request&lt;axum::body::Body&gt;) -&gt; impl std::future::Future&lt;Output = axum::http::Response&lt;http_body::combinators::box_body::UnsyncBoxBody&lt;axum::body::Bytes, axum::Error&gt;&gt;&gt; {handle_proxy}: axum::handler::Handler&lt;_, _, _&gt;` is not satisfied
   --&gt; src/main.rs:34:48
    |
34  |     let app = Router::new().route(&quot;/:key&quot;, get(handle_proxy));
    |                                            --- ^^^^^^^^^^^^ the trait `axum::handler::Handler&lt;_, _, _&gt;` is not implemented for fn item `fn(axum::Extension&lt;std::sync::Arc&lt;std::sync::RwLock&lt;std::path::PathBuf&gt;&gt;&gt;, axum::http::Request&lt;axum::body::Body&gt;) -&gt; impl std::future::Future&lt;Output = axum::http::Response&lt;http_body::combinators::box_body::UnsyncBoxBody&lt;axum::body::Bytes, axum::Error&gt;&gt;&gt; {handle_proxy}`
    |                                            |
    |                                            required by a bound introduced by this call
    |
    = help: the following other types implement trait `axum::handler::Handler&lt;T, S, B&gt;`:
              &lt;axum::handler::Layered&lt;L, H, T, S, B, B2&gt; as axum::handler::Handler&lt;T, S, B2&gt;&gt;
              &lt;axum::routing::MethodRouter&lt;S, B&gt; as axum::handler::Handler&lt;(), S, B&gt;&gt;
note: required by a bound in `axum::routing::get`
   --&gt; /home/mycroft/.cargo/registry/src/github.com-1ecc6299db9ec823/axum-0.6.18/src/routing/method_routing.rs:403:1
    |
403 | top_level_handler_fn!(get, GET);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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)

For more information about this error, try `rustc --explain E0277`.
error: could not compile `hello-rs-axum-failure` due to previous error

I understand that adding the line is breaking something, but why? I found out limiting the scope of the declaration (ie: putting the instruction in its own scope and copying the underlying variable) will help, but I'd really like to know why the error is happening.

答案1

得分: 1

在异步处理函数中的代码部分保留了cache值的guard

正如您发现的那样,这个guard不是Send,因此不能跨越await,因为执行可能在从await中恢复执行后继续在不同线程上进行。

解决这个问题有两种可能性:

  1. 更改代码,以便在await边界上不保持guard。(可能可行,也可能不可行或不可取。)

  2. 使用类似于tokio::sync::RwLock的异步并发结构,而不是标准库中的结构。这些结构可以跨越await边界持有。

英文:

The code in the async handler function is holding onto the guard for the cache value in the scope.

As you discovered, this guard is not Send, so it can't be held across awaits because the execution might continue on a different thread after resuming execution from an await.

Two possibilities to solve this:

  1. Change the code so you don't hold the guard across an await boundary. (May or may not be possible or desirable.)

  2. Use an async concurrency structure like tokio::sync::RwLock instead of the one in the standard library. These can be held across await boundaries.

huangapple
  • 本文由 发表于 2023年5月22日 23:15:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76307624.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定