Is it possible to create a macro to implement builder pattern methods? - rust

I have a builder pattern implemented for my struct:
pub struct Struct {
pub grand_finals_modifier: bool,
}
impl Struct {
pub fn new() -> Struct {
Struct {
grand_finals_modifier: false,
}
}
pub fn grand_finals_modifier<'a>(&'a mut self, name: bool) -> &'a mut Struct {
self.grand_finals_modifier = grand_finals_modifier;
self
}
}
Is it possible in Rust to make a macro for methods like this to generalize and avoid a lot of duplicating code? Something that we can use as the following:
impl Struct {
builder_field!(hello, bool);
}

After reading the documentation, I've come up with this code:
macro_rules! builder_field {
($field:ident, $field_type:ty) => {
pub fn $field<'a>(&'a mut self,
$field: $field_type) -> &'a mut Self {
self.$field = $field;
self
}
};
}
struct Struct {
pub hello: bool,
}
impl Struct {
builder_field!(hello, bool);
}
fn main() {
let mut s = Struct {
hello: false,
};
s.hello(true);
println!("Struct hello is: {}", s.hello);
}
It does exactly what I need: creates a public builder method with specified name, specified member and type.

To complement the already accepted answer, since it is 4 years old by now, you should check out the crate rust-derive-builder. It uses procedural macros to automatically implement the builder pattern for any struct.

Related

How to use the typestate pattern in other struct

I want to use the typestate pattern to define several states that allow some exclusive operations on each of them.
I'm using traits instead of an enum to allow further customizations.
So, I'm able to use this pattern until I try to include it inside a struct (the Session part) that is mutated when files are added, changed or removed.
trait IssueState {}
struct Open;
impl IssueState for Open {}
struct WIP {
elapsed_time: u32,
}
impl IssueState for WIP {}
struct Closed {
elapsed_time: u32,
}
impl IssueState for Closed {}
struct Issue<T: IssueState + ?Sized> {
state: Box<T>,
comments: Vec<String>,
}
impl<T: IssueState> Issue<T> {
pub fn comment<S: Into<String>>(&mut self, comment: S) -> &mut Self {
self.comments.push(comment.into());
self
}
}
impl Issue<Open> {
pub fn new() -> Self {
Self {
state: Box::new(Open),
comments: vec![],
}
}
pub fn start(self) -> Issue<WIP> {
Issue {
state: Box::new(WIP { elapsed_time: 0 }),
comments: self.comments,
}
}
}
impl Issue<WIP> {
pub fn work(&mut self, time: u32) -> &mut Self {
self.state.elapsed_time += time;
self
}
pub fn done(self) -> Issue<Closed> {
let elapsed_time = self.state.elapsed_time;
Issue {
state: Box::new(Closed { elapsed_time }),
comments: self.comments,
}
}
}
impl Issue<Closed> {
pub fn elapsed(&self) -> u32 {
self.state.elapsed_time
}
}
struct Session<T: IssueState> {
user: String,
current_issue: Issue<T>,
}
impl<T: IssueState> Session<T> {
pub fn new<S: Into<String>>(user: S, issue: Issue<T>) -> Self {
Self {
user: user.into(),
current_issue: issue,
}
}
pub fn comment<S: Into<String>>(&mut self, comment: S) {
self.current_issue.comment(comment);
}
}
impl Session<WIP> {
pub fn work(&mut self, time: u32) {
self.current_issue.work(time);
}
}
trait Watcher {
fn watch_file_create(&mut self);
fn watch_file_change(&mut self);
fn watch_file_delete(&mut self);
}
impl<T: IssueState> Watcher for Session<T> {
fn watch_file_create(&mut self) {
self.current_issue = Issue::<Open>::new();
}
fn watch_file_change(&mut self) {}
fn watch_file_delete(&mut self) {}
}
fn main() {
let open = Issue::<Open>::new();
let mut wip = open.start();
wip.work(10).work(30).work(60);
let closed = wip.done();
println!("Elapsed {}", closed.elapsed());
let mut session = Session::new("Reviewer", closed);
session.comment("It is OK");
session.watch_file_create();
}
Rust Playground (original)
Rust Playground (edited)
What can I do to fix the problems?
Is the typestate pattern limited to only some situations that do not depend a lot on external events? I mean, I'm trying to use it for processing events, but is it a dead end?, why?
Your Session has a Issue<dyn IssueState> member, but you want to implement its work method by calling Issue<WIP>'s work method. The compiler complains, because an Issue<dyn IssueState> is not (necessarily) a Issue<WIP> and so does not implement that method.

Rust: How to return a reference to an Rc<RefCell<HashMap<K, V>> value?

I'm trying to learn Rust and I'm having some problems with different smart pointers and stuff.
Here is my code:
pub struct MyMap<T> {
map: Rc<RefCell<HashMap<String, T>>>,
}
impl <T> MyMap<T> {
// Not entire sure if it's supposed to be Option<Ref<T>> or something else here.
pub fn get(&self, key: &str) -> Option<Ref<T>> {
todo!("What do I do here?")
}
}
The closest I've got is by searching the HashMap twice:
impl <T> MyMap<T> {
pub fn get(&self, key: &str) -> Option<Ref<T>> {
if self.map.borrow().contains_key(key) {
Some(Ref::map(self.map.borrow(), |m| m.get(key).unwrap()))
} else {
None
}
}
}
Which isn't very elegant to say the least.
Two solutions that come to my mind:
Use the unstable Ref::filter_map, which will potentially be stabilized in 1.63.0.
Use a context manager pattern, which circumvents the entire problem.
filter_map:
#![feature(cell_filter_map)]
use std::{
cell::{Ref, RefCell},
collections::HashMap,
rc::Rc,
};
pub struct MyMap<T> {
map: Rc<RefCell<HashMap<String, T>>>,
}
impl<T> MyMap<T> {
pub fn get(&self, key: &str) -> Option<Ref<T>> {
Ref::filter_map(self.map.borrow(), |map| map.get(key)).ok()
}
}
fn main() {
let map: MyMap<u32> = MyMap {
map: Rc::new(RefCell::new(HashMap::from([
("meaning".to_string(), 42),
("nice".to_string(), 69),
]))),
};
println!("{:?}", map.get("meaning"));
}
Some(42)
Context manager:
The idea here is that instead of returning a reference, you pass in a closure of the action that needs the value. That entirely circumvents the lifetime problem, because the variables inside of the get (or with_value in the example below) are still in scope while the closure gets executed.
use std::{cell::RefCell, collections::HashMap, rc::Rc};
pub struct MyMap<T> {
map: Rc<RefCell<HashMap<String, T>>>,
}
impl<T> MyMap<T> {
pub fn with_value<F, O>(&self, key: &str, f: F) -> O
where
F: FnOnce(Option<&T>) -> O,
{
f(self.map.borrow().get(key))
}
}
fn main() {
let map: MyMap<u32> = MyMap {
map: Rc::new(RefCell::new(HashMap::from([
("meaning".to_string(), 42),
("nice".to_string(), 69),
]))),
};
map.with_value("meaning", |value| {
println!("{:?}", value);
});
}
Some(42)

Allowing extension (beyond the crate) of implementation with event loop

Within the crate we can happily do something like this:
mod boundary {
pub struct EventLoop;
impl EventLoop {
pub fn run(&self) {
for _ in 0..2 {
self.handle("bundled");
self.foo();
}
}
pub fn handle(&self, message: &str) {
println!("{} handling", message)
}
}
pub trait EventLoopExtend {
fn foo(&self);
}
}
use boundary::EventLoopExtend;
impl EventLoopExtend for boundary::EventLoop {
fn foo(&self) {
self.handle("extended")
}
}
fn main() {
let el = boundary::EventLoop{};
el.run();
}
But if mod boundary were a crate boundary we get error[E0117]: only traits defined in the current crate can be implemented for arbitrary types.
I gather that a potential solution to this could be the New Type idiom, so something like this:
mod boundary {
pub struct EventLoop;
impl EventLoop {
pub fn run(&self) {
for _ in 0..2 {
self.handle("bundled");
self.foo();
}
}
pub fn handle(&self, message: &str) {
println!("{} handling", message)
}
}
pub trait EventLoopExtend {
fn foo(&self);
}
impl EventLoopExtend for EventLoop {
fn foo(&self) {
self.handle("unimplemented")
}
}
}
use boundary::{EventLoop, EventLoopExtend};
struct EventLoopNewType(EventLoop);
impl EventLoopExtend for EventLoopNewType {
fn foo(&self) {
self.0.handle("extended")
}
}
fn main() {
let el = EventLoopNewType(EventLoop {});
el.0.run();
}
But then the problem here is that the extended trait behaviour isn't accessible from the underlying EventLoop instance.
I'm still quite new to Rust, so I'm sure I'm missing something obvious, I wouldn't be surprised if I need to take a completely different approach.
Specifically in my case, the event loop is actually from wgpu, and I'm curious if it's possible to build a library where end users can provide their own "render pass" stage.
Thanks to #AlexN's comment I dug deeper into the Strategy Pattern and found a solution:
mod boundary {
pub struct EventLoop<'a, T: EventLoopExtend> {
extension: &'a T
}
impl<'a, T: EventLoopExtend> EventLoop<'a, T> {
pub fn new(extension: &'a T) -> Self {
Self { extension }
}
pub fn run(&self) {
for _ in 0..2 {
self.handle("bundled");
self.extension.foo(self);
}
}
pub fn handle(&self, message: &str) {
println!("{} handling", message)
}
}
pub trait EventLoopExtend {
fn foo<T: EventLoopExtend>(&self, el: &EventLoop<T>) {
el.handle("unimplemented")
}
}
}
use boundary::{EventLoop, EventLoopExtend};
struct EventLoopExtension;
impl EventLoopExtend for EventLoopExtension {
fn foo<T: EventLoopExtend>(&self, el: &EventLoop<T>) {
el.handle("extended")
}
}
fn main() {
let el = EventLoop::new(&EventLoopExtension {});
el.run();
}
The basic idea is to use generics with a trait bound. I think the first time I looked into this approach I was worried about type recursion. But it turns out passing the EventLoop object as an argument to EventLoopExtend trait methods is perfectly reasonable.

How to avoid orphan rules for repr transparent wrappers

My problem is I want to have a transparent wrapper and implemented Into<underlying> for it. Unfortunately, rust's orphan rules forbid it. Here is a simple example:
#[repr(transparent)]
pub struct MyWrapper<T>(pub T);
impl<T> Into<T> for MyWrapper<T> {
fn into(self) -> T {
self.0
}
}
The question is is there any way I can implement it? I'm using macro to generate impl for all types I'm currently using but it looks very awkward and dirty.
You can implement the Deref trait instead. The Deref docs contain the following example which is almost identical to your code:
use std::ops::Deref;
struct DerefExample<T> {
value: T
}
impl<T> Deref for DerefExample<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
fn main() {
let x = DerefExample { value: 'a' };
assert_eq!('a', *x);
}
You can implement it in a regular impl block:
#[repr(transparent)]
pub struct MyWrapper<T>(pub T);
impl<T> MyWrapper<T> {
pub fn into(self) -> T {
self.0
}
}
fn main() {
let wrapped : MyWrapper<f32> = MyWrapper::<f32>(3.4f32);
let unwrapped : f32 = wrapped.into();
println!("{}", unwrapped);
}

How does Rust's struct hold and use a object?

I need to use a struct to hold a serde_json object, and use it later.
extern crate serde_json;
use serde_json::builder::ObjectBuilder;
struct MyStruct {
builder: ObjectBuilder,
}
impl MyStruct {
fn new() -> MyStruct {
MyStruct { builder: ObjectBuilder::new() }
}
fn add_string_member(&self, name: &str, value: &str) {
self.builder.insert(name, value); //here compile error
}
}
fn main() {
let s = MyStruct::new();
s.add_string_member("name", "value");
}
But I get the error
error: cannot move out of borrowed content [E0507]
The methods in ObjectBuilder take self by value. Since you can't move something out of a borrowed pointer, the easy solution is to make your methods on MyStruct take self by value as well.
Also, ObjectBuilder's methods return a new ObjectBuilder with the changes. You can wrap that return value into a new MyStruct, which you can return from your methods.
extern crate serde_json;
use serde_json::builder::ObjectBuilder;
struct MyStruct {
builder: ObjectBuilder,
}
impl MyStruct {
fn new() -> MyStruct {
MyStruct { builder: ObjectBuilder::new() }
}
fn add_string_member(self, name: &str, value: &str) -> MyStruct {
MyStruct { builder: self.builder.insert(name, value) }
}
}
fn main() {
let s = MyStruct::new();
let s = s.add_string_member("name", "value");
}
If MyStruct also contains other members that you'd like to carry on into the new MyStruct, you can use a shortcut syntax to initialize the remaining fields of MyStruct from an existing instance:
fn add_string_member(self, name: &str, value: &str) -> MyStruct {
MyStruct { builder: self.builder.insert(name, value), ..self }
}
Here, the builder field of the new MyStruct will be set to the specified expression, and all other fields will be moved from self. (The .. syntax accepts any expression of the proper type, not just self.)

Resources