英文:
Dedup logic without getting "cannot return value referencing local variable `queues` returns a value referencing data owned by the current function"
问题
Here is the translated code part you requested:
我有以下的结构体:
```rust
use std::sync::Mutex;
pub struct Message {
id: String,
content: String,
last_read: Option<String>,
uuid: String,
}
impl Message {
pub fn get_uuid(&self) -> String {
self.uuid.to_owned()
}
}
pub struct Queue {
queue: Vec<Message>, // the actual queue
read_timeout: u32, // the amount of time a message is hidden from consumers
size: u32, // should always be the same as queue.len()
uuid: String,
}
pub struct AppState {
pub queues: Mutex<Vec<Queue>>,
}
最后,我有一些处理数据的函数。正如你下面所见,它们在获取 queue
后有重复的逻辑。它们在获取 queue
的方式相同,但在获取 queue
之后的操作不同。
pub async fn add_message(
data: AppState
) {
let queue_uuid = &String::from("my uuid");
let queue_mutex = data.queues.lock();
let mut queues = match queue_mutex {
Ok(q) => q,
Err(_) => return,
};
// 找到匹配的队列
let queue = queues.iter_mut().find(|q| q.uuid == *queue_uuid);
}
pub async fn get_message(
data: AppState
) {
let queue_uuid = &String::from("my uuid");
let queue_mutex = data.queues.lock();
let mut queues = match queue_mutex {
Ok(q) => q,
Err(_) => return,
};
// 找到匹配的队列
let queue = queues.iter_mut().find(|q| q.uuid == *queue_uuid);
// 一旦找到队列,对其进行一些操作
}
我想要去除这种重复的逻辑。
impl AppState {
pub fn get_queues(&self) -> &Mutex<Vec<Queue>> {
&self.queues
}
pub fn get_queue_by_id(&mut self, uuid: String) -> Result<&mut Queue, String> {
let queue_mutex = self.queues.lock();
let mut queues = match queue_mutex {
Ok(q) => q,
Err(_) => return Err(String::from("哎呀!出了点问题")),
};
match queues.iter_mut().find(|q| q.uuid == uuid) {
None => Err(format!("找不到uuid为{}的队列", uuid)),
Some(q) => Ok(q),
}
}
}
然而,这会导致一个错误,因为不能返回 q
,否则它将在函数返回时被丢弃。是否有好的解决方案呢?
P.S:我知道很多这些可以更高效 - 例如,使用哈希映射而不是向量来存储当前的队列。我是 Rust 的新手,并打算对这些代码进行大量重构,所以任何反馈都有帮助!谢谢!
<details>
<summary>英文:</summary>
I have the following structs:
use std::sync::Mutex;
pub struct Message {
id: String,
content: String,
last_read: Option<String>,
uuid: String,
}
impl Message {
pub fn get_uuid(&self) -> String {
self.uuid.to_owned()
}
}
pub struct Queue {
queue: Vec<Message>, // the actual queue
read_timeout: u32, // the amount of time a message is hidden from consumers
size: u32, // should always be the same as queue.len()
uuid: String,
}
pub struct AppState {
pub queues: Mutex<Vec<Queue>>,
}
Finally, I have some functions that do something with the data. They have duplicate logic, as you can see below. What they do after they get the `queue` is different, but the way to retrieve the queue is the same.
pub async fn add_message(
data: AppState
) {
let queue_uuid = &String::from("my uuid");
let queue_mutex = data.queues.lock();
let mut queues = match queue_mutex {
Ok(q) => q,
Err(_) => return,
};
// find matching queue
let queue = queues.iter_mut().find(|q| q.uuid == *queue_uuid);
}
pub async fn get_message(
data: AppState
) {
let queue_uuid = &String::from("my uuid");
let queue_mutex = data.queues.lock();
let mut queues = match queue_mutex {
Ok(q) => q,
Err(_) => return,
};
// find matching queue
let queue = queues.iter_mut().find(|q| q.uuid == *queue_uuid);
// once the queue is found, do some stuff with it
}
I would like to dedup this logic.
impl AppState {
pub fn get_queues(&self) -> &Mutex<Vec<Queue>> {
&self.queues
}
pub fn get_queue_by_id(&mut self, uuid: String) -> Result<&mut Queue, String> {
let queue_mutex = self.queues.lock();
let mut queues = match queue_mutex {
Ok(q) => q,
Err(_) => return Err(String::from("Whoops! Something went wrong")),
};
match queues.iter_mut().find(|q| q.uuid == uuid) {
None => Err(format!("Could not find queue with uuid {}", uuid)),
Some(q) => Ok(q),
}
}
}
However, this will yield an error because I cannot return `q` as it will be dropped when the function returns. Is there a good solution to this?
P.S: I know that a lot of this could be more efficient - i.e. using a hash map instead of vector to store the current queues. I am a Rust newbie and intend on refactoring a lot of this, so any feedback is helpful! Thanks!
</details>
# 答案1
**得分**: 1
除了在`async`上下文中比`std::sync::Mutex`更合适之外,`futures`变体还有另一个好处,它允许您的使用模式,通过具有[`MutexGuard::map`](https://docs.rs/futures/0.3.28/futures/lock/struct.MutexGuard.html#method.map)来返回`MappedMutexGuard<Vec<Queue>, Queue>`:
```rust
impl AppState {
pub async fn get_queue_by_id(
&mut self,
uuid: String,
) -> Result<MappedMutexGuard<Vec<Queue>, Queue>, String> {
let mut queues = self.queues.lock().await;
match queues.iter_mut().position(|q| q.uuid == uuid) {
None => Err(format!("Could not find queue with uuid {}", uuid)),
Some(p) => Ok(MutexGuard::map(queues, |q| &mut q)),
}
}
}
据我所知,在std
中的Mutex
还不能(尚未?)实现同样的功能,可参见Rust内部讨论。
英文:
In addition to being more appropriate in async
contexts than std::sync::Mutex
anyways the futures
variant has another benefit, it allows your usage pattern by having a MutexGuard::map
which let's you return a MappedMutexGuard<Vec<Queue>, Queue>
:
impl AppState {
pub async fn get_queue_by_id(
&mut self,
uuid: String,
) -> Result<MappedMutexGuard<Vec<Queue>, Queue>, String> {
let mut queues = self.queues.lock().await;
match queues.iter_mut().position(|q| q.uuid == uuid) {
None => Err(format!("Could not find queue with uuid {}", uuid)),
Some(p) => Ok(MutexGuard::map(queues, |q| &mut q)),
}
}
}
As far as I know the same is not (yet?) possible with the Mutex
in std
, see this Rust internals discussion.
答案2
得分: 1
Here are the translations:
如果您只想返回互斥锁的内容,可以返回 impl DerefMut<Target = _>
。这将允许您在不更改函数签名的情况下交换您使用的同步包装器。
这不适用于获取特定队列,但另一个答案解释了如何使用未来的 crate 来实现这一点。
使用标准互斥锁的一种方法是接受修改所选队列的闭包。
由于这采用常规的 FnOnce
闭包而不是 Future
,因此即使在单线程运行时环境中,这也确保了锁不会在 await
期间保持。
请注意,这只是代码的翻译部分,没有其他内容。
英文:
If you want to return just a mutex's contents, you can return impl DerefMut<Target = _>
. This will allow you to swap out the synchronization wrapper you use without changing the function's signature.
pub fn queues(&self) -> Result<impl Deref<Target = Vec<Queue>> + '_, String> {
self.queues.lock().map_err(|_| "Mutex poisoned".to_string())
}
// The same since `Mutex` doesn't differentiate between readers and writers. However,
// if a `RwLock` is used instead, this would be different from `queues`.
pub fn queues_mut(&self) -> Result<impl DerefMut<Target = Vec<Queue>> + '_, String> {
self.queues.lock().map_err(|_| "Mutex poisoned".to_string())
}
This doesn't work for getting a specific queue, but the other answer explains how you can do this with the future crate's mutex.
One way to do this with the std mutex is to take a closure that modifies the selected queue.
pub fn modify_queue_by_id<F, R>(&mut self, uuid: String, f: F) -> Result<R, String>
where
F: FnOnce(&mut Queue) -> R,
{
let mut queues = self
.queues
.lock()
.map_err(|_| "Whoops! Something went wrong".to_string())?;
let queue = queues
.iter_mut()
.find(|q| q.uuid == uuid)
.ok_or_else(|| format!("Could not find queue with uuid {}", uuid))?;
Ok(f(queue))
}
Since this takes a regular FnOnce
closure and not a Future
, this ensures that the lock isn't held across an await
, even in a single-threaded runtime context.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论