In the following Keymap
struct, the prompt
field is optional:
pub struct Keymap {
pub key: char,
pub description: String,
pub command: String,
pub prompt: Option<String>,
}
And I've created two methods. One that creates a Keymap
without prompt, and another that creates a Keymap
with a prompt:
impl Keymap {
pub fn new(key: char, description: &str, command: &str) -> Self {
Self {
key,
description: description.to_string(),
command: command.to_string(),
prompt: None,
}
}
pub fn with_prompt(key: char, description: &str, command: &str, prompt: &str) -> Self {
Self {
key,
description: description.to_string(),
command: command.to_string(),
prompt: Some(prompt.to_string()),
}
}
}
Now, I want to make the description
field optional too (it not present, it will default to the same value as command
). To do that, I would have to create a with_description
method and a with_prompt_and_description
method.
I feel like I have to use another design pattern here?
Have a look at the Builder pattern. Can't explain it rn but just google "builder pattern rust"
3 Likes
jofas
July 3, 2023, 3:50pm
3
Yes, I'd use the builder pattern for this as well. Something like this, probably:
#[derive(Debug, Default)]
pub struct Keymap {
pub key: char,
pub command: String,
pub description: Option<String>,
pub prompt: Option<String>,
}
impl Keymap {
pub fn new<S: AsRef<str>>(key: char, command: S) -> Self {
Self {
key,
command: command.as_ref().to_owned(),
..Default::default()
}
}
pub fn with_prompt<S: AsRef<str>>(mut self, prompt: S) -> Self {
self.prompt = Some(prompt.as_ref().to_owned());
self
}
pub fn with_description<S: AsRef<str>>(mut self, description: S) -> Self {
self.description = Some(description.as_ref().to_owned());
self
}
}
2 Likes
Thanks for the example!
One question: why did you use AsRef
as opposed to just this?
pub struct KeymapBuilder {
key: char,
description: Option<String>,
command: String,
prompt: Option<String>,
}
impl KeymapBuilder {
pub fn new(key: char, command: &str) -> Self {
Self {
key,
description: None,
command: command.to_string(),
prompt: None,
}
}
pub fn with_description(mut self, description: &str) -> Self {
self.description = Some(description.to_string());
self
}
pub fn with_prompt(mut self, prompt: &str) -> Self {
self.prompt = Some(prompt.to_string());
self
}
pub fn build(self) -> Keymap {
Keymap {
key: self.key,
description: self.description.unwrap_or_else(|| self.command.clone()),
command: self.command,
prompt: self.prompt,
}
}
}
It's a convenience that lets you also pass in a String
without needing to put a &
in front of it.
Often if the field is an owned String
(i.e. prompt: Option<String>
), I'll make the with_prompt()
method accept a prompt: impl Into<String>
. That way if the caller already has an owned String
they can pass that in without making any copies, and for things like testing where you use hard-coded string literals, it'll let you pass in that &str
without cluttering your code with loads of to_string()
calls.
2 Likes
system
Closed
October 2, 2023, 6:24am
6
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.