Member function callable in function but not in main

In messing with some IMAP rust examples, I'm running into a problem.
I have what appears to be two identical lines of code:

let body = message.body().expect("message did not have a body!");

appears twice in the program. When it is inside the fetch_inbox_top function it has no problem. But inside the main function I get an error about the body method:

   |
   |                     let body = message.body().expect("message did not have a body!");
   |                                        ^^^^ method not found in `&Fetches`

It finds the body method fine when called within fetch_inbox_top .... any idea why?

Full code:

extern crate imap;

use imap::types::UnsolicitedResponse;

mod mine;

struct Opt{
    server: String,
    port: u16,
    username: String,
    password: String,
    mailbox: String,
}

fn main() {
    let opt = Opt{
        server: String::from("imap.mail.us-west-2.awsapps.com"),
        port: 993,
        username: String::from("removed"),
        password: String::from("removed"),
        mailbox: String::from("INBOX"),
    };

    let client = imap::ClientBuilder::new(opt.server.clone(), opt.port.clone())
        .native_tls()
        .expect("Could not connect to imap server");

    let mut imap = client
        .login(opt.username.clone(), opt.password.clone())
        .expect("Could not authenticate");

    imap.debug = true;

    imap.select(&opt.mailbox).expect("Could not select mailbox");

    let mut num_responses = 0;
    let max_responses = 0;

    fetch_inbox_top(&opt).unwrap();

    let idle_result = imap.idle().wait_while(|response| {
        num_responses += 1;

        println!("IDLE response #{}: {:?}", num_responses, response);

        (|| {
            match response {
                UnsolicitedResponse::Exists(num) => {
                    let messages = imap.fetch("1", "RFC822");

                    let message = if let Some(m) = messages.iter().next() {
                        m
                    } else {
                        return;
                    };
                
                    // extract the message's body
                    let body = message.body().expect("message did not have a body!");
                    let body = std::str::from_utf8(body)
                        .expect("message was not valid utf-8")
                        .to_string();
                
                    println!("{}", body);
                },
                _ => {
                    println!("unhandled {:?}", response);
                }
            }
        })();

        if max_responses != 0 && num_responses >= max_responses {
            // Stop IDLE
            false
        } else {
            // Continue IDLE
            true
        }
    });

    match idle_result {
        Ok(reason) => println!("IDLE finished normally {:?}", reason),
        Err(e) => println!("IDLE finished with error {:?}", e),
    }

    imap.logout().expect("Could not log out");
}

fn fetch_inbox_top(opt: &Opt) -> imap::error::Result<Option<String>> {

    let client = imap::ClientBuilder::new(opt.server.clone(), opt.port.clone())
        .native_tls()
        .expect("Could not connect to imap server");

    let mut imap_session = client
        .login(opt.username.clone(), opt.password.clone())
        .expect("Could not authenticate");

    // we want to fetch the first email in the INBOX mailbox
    imap_session.select("INBOX")?;

    // fetch message number 1 in this mailbox, along with its RFC822 field.
    // RFC 822 dictates the format of the body of e-mails
    let messages = imap_session.fetch("1", "RFC822")?;
    let message = if let Some(m) = messages.iter().next() {
        m
    } else {
        return Ok(None);
    };

    // extract the message's body
    let body = message.body().expect("message did not have a body!");
    let body = std::str::from_utf8(body)
        .expect("message was not valid utf-8")
        .to_string();

    // be nice to the server and log out
    imap_session.logout()?;

    Ok(Some(body))
}

Code for body:

    /// The bytes that make up this message, included if `BODY[]` or `RFC822` was included in the
    /// `query` argument to `FETCH`. The bytes SHOULD be interpreted by the client according to the
    /// content transfer encoding, body type, and subtype.
    pub fn body(&self) -> Option<&[u8]> {
        self.fetch.iter().find_map(|av| match av {
            AttributeValue::BodySection {
                section: None,
                data: Some(body),
                ..
            }
            | AttributeValue::Rfc822(Some(body)) => Some(&**body),
            _ => None,
        })
    }

I think I found the issue. The lack of a ? at the end of

let messages = imap.fetch("1", "RFC822");

is causing the return value to not be what was expected. I need to wrap my rustacean claws a bit more firmly around that syntax.

Missing the ? on the .fetch call creates a Result. Then Result::iter is called, creating an iterator with a single element (or none if there’s an error). Since the item message is (a reference to) the whole Fetches and not a single Fetch<'_>, it’s complaining about missing the .body() method.

1 Like