我使用 Axum 框架构建一个简单的休息服务器。我想要一种“应用程序状态”,其中包含一些可重用的组件,供我的一些处理程序重用/使用。
#![allow(incomplete_features)]
#![feature(async_fn_in_trait)]
use axum::{routing::post, Router};
use std::{net::SocketAddr, sync::Arc};
mod modules;
use modules::auth::handlers::login;
pub struct AppState<'a> {
user_credentials_repository: UserCredentialsRepository<'a>,
}
pub struct UserCredentialsRepository<'a> {
shared_name: &'a mut String,
}
impl<'a> UserCredentialsRepository<'a> {
pub fn new(shared_name: &'a mut String) -> UserCredentialsRepository<'a> {
UserCredentialsRepository { shared_name }
}
}
#[tokio::main]
async fn main() {
let mut name = String::from("Tom");
let mut user_credentials_repository = UserCredentialsRepository::new(&mut name);
let shared_state = Arc::new(AppState {
user_credentials_repository,
});
let app = Router::new()
.route("/login", post(login))
.with_state(shared_state);
let addr = SocketAddr::from(([127, 0, 0, 1], 7777));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
Run Code Online (Sandbox Code Playgroud)
基本上我想要实现的是重用数据库会话/连接实例。我首先尝试共享一个字符串来测试它,但它似乎不起作用。我收到以下错误:
`name` dropped here while still borrowed
Run Code Online (Sandbox Code Playgroud)
和
argument requires that `name` is borrowed for `'static`
Run Code Online (Sandbox Code Playgroud)
我对生命周期的工作方式有一定的了解,但我猜测“.with_state(...)”内部的某些内容取得了我传递的值的所有权并试图放弃它?
有两个问题:生命周期和共享可变性。
shared_state可能会被发送到另一个线程(由 tokio 运行时),所以 rust 不知道shared_state当 main 函数删除时是否仍然存在name。&即使它不是&mut.Clone,但&mut类型未实现。这是因为,如果您收到多个请求,则可能有多个处理程序尝试同时访问同一状态。&mut对于同一变量同时存在多个变量是未定义的行为,并且在安全代码中通过&mut不允许存在来强制执行这一行为Clone。您尝试通过放入状态来解决#1 问题Arc在这里不起作用,因为它仍然包含引用。您需要将引用替换为Arc.
#2 的解决方案是使用共享可变性构造,例如Mutexor RwLock。
首先,您需要删除引用:
pub struct UserCredentialsRepository {
shared_name: String,
}
impl UserCredentialsRepository {
pub fn new(shared_name: String) -> UserCredentialsRepository {
UserCredentialsRepository { shared_name }
}
}
Run Code Online (Sandbox Code Playgroud)
虽然您可以直接替换&mut为Mutex并使其正常工作,但我会从更简单的事情开始。
让我们保留Arc并main用 包裹user_credentials_repository起来Mutex:
pub struct AppState {
user_credentials_repository: Mutex<UserCredentialsRepository>,
}
Run Code Online (Sandbox Code Playgroud)
然后在你的login函数中的某个地方,你需要lock读取Mutex和写入它:
let lock = state.user_credentials_repository.lock().unwrap();
lock.shared_name = "New name".to_string();
Run Code Online (Sandbox Code Playgroud)
这应该可以按预期编译和工作。
如果您有许多可以单独访问的单独项目,您可能需要将 放在Mutex每个项目上(类似于您的原始结构):
pub struct AppState {
user_credentials_repository: UserCredentialsRepository,
}
pub struct UserCredentialsRepository {
shared_name: Mutex<String>,
other_state: Mutex<String>,
}
Run Code Online (Sandbox Code Playgroud)
然后,单独的线程可以锁定单独的项目,而不会阻塞其中之一。
如果您期望频繁读取数据而不频繁写入数据,则可以使用RwLock,它允许任意数量的读锁,只要没有写锁即可:
pub struct AppState {
user_credentials_repository: RwLock<UserCredentialsRepository>,
}
Run Code Online (Sandbox Code Playgroud)
用法与 几乎相同Mutex,但不是lock方法,而是read和write。
| 归档时间: |
|
| 查看次数: |
1004 次 |
| 最近记录: |