Dedup logic without getting "cannot return value referencing local variable `queues` returns a value referencing data owned by the current function"

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

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) =&gt; q,
    Err(_) =&gt; 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) =&gt; q,
    Err(_) =&gt; 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(&amp;mut self, uuid: String) -&gt; Result&lt;&amp;mut Queue, String&gt; {
    let queue_mutex = self.queues.lock();
    let mut queues = match queue_mutex {
        Ok(q) =&gt; q,
        Err(_) =&gt; return Err(String::from(&quot;Whoops! Something went wrong&quot;)),
    };
    match queues.iter_mut().find(|q| q.uuid == uuid) {
        None =&gt; Err(format!(&quot;Could not find queue with uuid {}&quot;, uuid)),
        Some(q) =&gt; 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&lt;Vec&lt;Queue&gt;, Queue&gt;`:

```rust
impl AppState {
    pub async fn get_queue_by_id(
        &amp;mut self,
        uuid: String,
    ) -&gt; Result&lt;MappedMutexGuard&lt;Vec&lt;Queue&gt;, Queue&gt;, String&gt; {
        let mut queues = self.queues.lock().await;
        match queues.iter_mut().position(|q| q.uuid == uuid) {
            None =&gt; Err(format!(&quot;Could not find queue with uuid {}&quot;, uuid)),
            Some(p) =&gt; Ok(MutexGuard::map(queues, |q| &amp;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&lt;Vec&lt;Queue&gt;, Queue&gt;:

impl AppState {
    pub async fn get_queue_by_id(
        &amp;mut self,
        uuid: String,
    ) -&gt; Result&lt;MappedMutexGuard&lt;Vec&lt;Queue&gt;, Queue&gt;, String&gt; {
        let mut queues = self.queues.lock().await;
        match queues.iter_mut().position(|q| q.uuid == uuid) {
            None =&gt; Err(format!(&quot;Could not find queue with uuid {}&quot;, uuid)),
            Some(p) =&gt; Ok(MutexGuard::map(queues, |q| &amp;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&lt;Target = _&gt;. This will allow you to swap out the synchronization wrapper you use without changing the function's signature.

pub fn queues(&amp;self) -&gt; Result&lt;impl Deref&lt;Target = Vec&lt;Queue&gt;&gt; + &#39;_, String&gt; {
    self.queues.lock().map_err(|_| &quot;Mutex poisoned&quot;.to_string())
}

// The same since `Mutex` doesn&#39;t differentiate between readers and writers. However,
// if a `RwLock` is used instead, this would be different from `queues`.
pub fn queues_mut(&amp;self) -&gt; Result&lt;impl DerefMut&lt;Target = Vec&lt;Queue&gt;&gt; + &#39;_, String&gt; {
    self.queues.lock().map_err(|_| &quot;Mutex poisoned&quot;.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&lt;F, R&gt;(&amp;mut self, uuid: String, f: F) -&gt; Result&lt;R, String&gt;
where
    F: FnOnce(&amp;mut Queue) -&gt; R,
{
    let mut queues = self
        .queues
        .lock()
        .map_err(|_| &quot;Whoops! Something went wrong&quot;.to_string())?;

    let queue = queues
        .iter_mut()
        .find(|q| q.uuid == uuid)
        .ok_or_else(|| format!(&quot;Could not find queue with uuid {}&quot;, 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.

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

发表评论

匿名网友

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

确定