Reputation: 2509
I have a Message
struct which have a field I want to modify when calling a method on it.
This Message
struct have an attachments
field, which is an Option<Vec<Attachment>>
.
My goal is to be able to call a method on Message
to push an Attachment
object to the attachments
Vec
.
#[derive(Debug, Serialize, Deserialize)]
pub struct Message {
/// The verified sender email address
#[serde(rename = "FromEmail")]
pub from_email: String,
/// The name of the sender
#[serde(rename = "FromName")]
pub from_name: String,
/// The subject of the email
#[serde(rename = "Subject")]
pub subject: Option<String>,
/// The raw text content of the email
#[serde(rename = "Text-part")]
pub text_part: String,
/// The HTML content of the email
#[serde(rename = "Html-part")]
pub html_part: Option<String>,
#[serde(rename = "Recipients")]
pub recipients: Vec<Recipient>,
#[serde(rename = "Attachments")]
pub attachments: Option<Vec<Attachment>>,
#[serde(rename = "Inline_attachments")]
pub inline_attachments: Option<Vec<InlineAttachments>>,
}
The following is my current implementation on the method that will push Attachment
objects to the Option<Vec<Attachment>>
:
pub fn attach(&mut self, attachment: Attachment) {
// if theres no attachments already
// initialize the attachments vector
if self.attachments.is_none() {
let mut attachments = Vec::new();
attachments.push(attachment);
self.attachments = Some(attachments);
} else {
// Where this is invalid as theres no `clone` for `Option<Vec<Attachment>>`
let attachments = self.attachments.clone();
attachments.push(attachment);
}
}
This is the implementation for the Attachment
struct
:
use serde::{Deserialize, Serialize};
/// An email attachment
#[derive(Debug, Serialize, Deserialize)]
pub struct Attachment {
#[serde(rename = "Content-type")]
pub content_type: String,
#[serde(rename = "Filename")]
pub filename: String,
pub content: String,
}
The issue is on the else
block, where the attachents
Option
field is unwrapped, then an attachment
object is pushed to the Vec
.
The problem is that Attachment
is unable to implement the Copy
trait.
Which is the correct approach for scenarios like this in Rust?
Thanks in advance!
Upvotes: 1
Views: 787
Reputation: 76774
Option
has a wide variety of helper methods which can help here. You basically want to mutate the Vec
that's already there if there is one, and if not, create one and mutate it. You can do that with Option::get_or_insert_with
, which returns a &mut Vec<Attachment>
. With that, you can push the item onto the end. A minimal compilable example looks like this:
pub struct Message {
pub attachments: Option<Vec<Attachment>>,
}
#[derive(Debug)]
pub struct Attachment {
pub content_type: String,
pub filename: String,
pub content: String,
}
impl Message {
pub fn attach(&mut self, attachment: Attachment) {
self.attachments
.get_or_insert_with(Vec::new)
.push(attachment);
}
}
fn main() {
let mut m = Message { attachments: None };
let a = Attachment {
content_type: "application/json".into(),
filename: "foo.json".into(),
content: "{}".into(),
};
m.attach(a);
assert_eq!(m.attachments.unwrap()[0].content, "{}");
}
If you're using is_some
or is_none
(or, for Result
, is_ok
or is_err
) in an if-else situation, that's usually an indication that you may want to rethink what you're doing, since that's not usually very idiomatic in Rust. Oftentimes, there's a helper method that does what you want in a simpler, easier to use way.
Upvotes: 6