对铁 rust 来说非常陌生.我希望使用指定的查询从Gmail下载邮箱附件,我有一个粗略的同步代码,功能和我希望使用多线程使用?Tokio::Sawn?(因为项目的其余部分已经使用Tokio)当前的多线程?代码如下:

struct EmailInterface {
    pub gmail: Gmail<HttpsConnector<HttpConnector>>,
}

impl EmailInterface {
    async fn new(path_to_key: String) -> Result<EmailInterface, Box<dyn error::Error>> {
        let authenticator =
            ServiceAccountAuthenticator::builder(read_service_account_key(path_to_key).await?)
                .build()
                .await?;

        let gmail = Gmail::new(
            hyper::Client::builder().build(
                hyper_rustls::HttpsConnectorBuilder::new()
                    .with_native_roots()
                    .https_or_http()
                    .enable_http1()
                    .enable_http2()
                    .build(),
            ),
            authenticator,
        );

        Ok(EmailInterface { gmail })
    }

    pub async fn get_attachments(
        self,
        query: &str,
    ) -> Result<(), Box<dyn std::error::Error>> {
        if let Some(messages_list) = self
            .gmail
            .users()
            .messages_list("me")
            .q(query)
            .include_spam_trash(false)
            .doit()
            .await?
            .1
            .messages
        {
            let messages_id_list: Vec<String> = messages_list
                .into_iter()
                .flat_map(|message| message.id)
                .collect();

            for id in messages_id_list {
                tokio::spawn(async move {

                    let message = self.gmail
                        .users()
                        .messages_get("me", id.as_str())
                        .doit()
                        .await
                        .unwrap_or_default()
                        .1;

                    let message = self
                        .gmail
                        .users()
                        .messages_get("me", id.as_str())
                        .doit()
                        .await
                        .unwrap_or_default()
                        .1;
                    for part in message.payload {
                        if let Some(part_body) = part.body {
                            if let Some(attachment_id) = part_body.attachment_id {
                                let attachment = self
                                    .gmail
                                    .users()
                                    .messages_attachments_get(
                                        "me",
                                        id.as_str(),
                                        attachment_id.as_str(),
                                    )
                                    .doit()
                                    .await
                                    .unwrap_or_default();
                                let data = general_purpose::STANDARD
                                    .decode(&attachment.1.data.unwrap_or_default())
                                    .unwrap();
                                std::fs::write(part.filename.expect("Should have a name"), &data)
                                    .unwrap();
                            }
                        }
                    }
                });
            }
        }
        Ok(())
    }
}

我目前收到的错误

use of moved value: `self.gmail`
move occurs because `self.gmail` has type `Gmail<HttpsConnector<HttpConnector>>`, which does not implement the `Copy` trait

如果有更流畅的方式做这件事,我会洗耳恭听.

我也try 过使用ThreadPool Crate&amp;ChatGPT来寻求建议,但后者总是以错误告终.

推荐答案

grumble grumbleMRE grumble

首先,我给几乎所有的 rust 蚀初学者一个建议:不要依赖您的IDE来获得好的错误消息.阅读cargo check/cargo watch的输出,它通常会提供更多信息,也更有帮助:

error[E0382]: use of moved value: `self.gmail`
  --> src/main.rs:52:41
   |
52 |                    tokio::spawn(async move {
   |   _________________________________________^
53 |  |                     let message = self
   |  |___________________________________-
54 | ||                         .gmail
   | ||______________________________- use occurs due to use in generator
55 |  |                         .users()
...   |
91 |  |                     }
92 |  |                 });
   |  |_________________^ value moved here, in previous iteration of loop
   |
   = note: move occurs because `self.gmail` has type `Gmail<HttpsConnector<HttpConnector>>`, which does not implement the `Copy` trait

在本例中,它告诉您它需要乘以self.gmail,因为有一个for循环,但它只有一个.要避免这种情况,您需要知道的是,如果某个东西没有实现Copy,它通常仍然实现Clone.不同的是,编译器可以自由地使用Copy来避免移动,但您需要手动使用Clone.在本例中,您可以使用Clone for each 派生的将来创建gmail客户端的专用实例.

for id in messages_id_list {
    let gmail = self.gmail.clone();
    tokio::spawn(async move {
        let message = gmail
            .users()
            .…
        // replace all the other instances of self.gmail as well

这将使您的代码编译,但它还不能工作:tokio::spawn启动任务,但它不能确保它们在您的程序退出之前完成:

不能保证派生的任务将执行到完成.当运行时关闭时,无论该任务的生命周期如何,所有未完成的任务都将被丢弃.

这里的"运行时"是您在注释#[tokio::main]时创建的,它将在main函数退出时关闭.

此外,如果你有很多邮件,我怀疑Gmail会因为你同时收到数千个请求而生气.

要同时解决这两个问题,请执行以下操作:

use futures::{StreamExt, TryStreamExt};

futures::stream::iter(&messages_id_list)
    .map(|id| async {
        let message = self
            .gmail
            .users()
        // …
        Ok::<_, String>(()) // You don't have error handling yet, so I'll use String as a dummy type.
    })
    .buffer_unordered(8)
    .try_collect::<()>()
    .await?;

buffer_unordered部分确保最大限度地运行固定数量的消息获取程序,await部分确保在await/get_attachments完成之前完成所有操作.请注意,也不再需要clone()move,因为Rust现在知道self将比消息获取者更长寿,所以您可以从那里borrow self.

最后:我认为async是Rust中不太好用的部分之一.你一开始就选了一件很难的事.

Rust相关问答推荐

在Rust中创建可变片段的可变片段的最有效方法是什么?

如何创建引用构造函数拥有的变量的对象?

何时可以在Rust中退出异步操作?

修改切片/引用数组

为什么这是&q;,而让&q;循环是无限循环?

是否可以在不切换到下一个位置的情况下获得迭代器值:

我是否可以在Ruust中修改 struct 实例上的字符串,以使其在修改后具有相同的字符串生存期?

如果变量本身不是None,如何返回;如果没有,则返回None&Quot;?

为什么Option类型try块需要类型注释?

为什么在 Allocator API 中 allocate() 使用 `[u8]` 而 deallocate 使用 `u8` ?

Rust:为什么 &str 不使用 Into

当推送到 HashMap 中的 Vector 时,类型 `()` 无法取消引用

Rust:为什么 Pin 必须持有指针?

(let b = MyBox(5 as *const u8); &b; ) 和 (let b = &MyBox(5 as *const u8); ) 之间有什么区别

bcrypt 有长度限制吗?

&str 的编译时拆分是否可能?

如何从 many0 传播 Nom 失败上下文?

如果我立即等待,为什么 `tokio::spawn` 需要一个 `'static` 生命周期?

返回 &str 但不是 String 时,borrow 时间比预期长

如何从 Rust 应用程序连接到 Docker 容器中的 SurrealDB?