英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论