Crate destructure

source ·
Expand description

Automation of Destructure Pattern

destructure is a automation library for destructure pattern.

Usage

use destructure::Destructure;

#[derive(Destructure)]
pub struct Book {
    id: u64,
    name: String,
    stocked_at: String,
    author: String,
    // ... too many fields...
}

fn main() {
    let book = Book {
        id: 1234_5678_9999_0000u64,
        name: "name".to_string(),
        stocked_at: "2023/01/03".to_string(),
        author: "author".to_string()
    };

    // Auto generate
    let des: DestructBook = book.into_destruct();

    println!("{:?}", des.id);
}

What is destructure pattern?

A structure with too many fields makes it hard to call constructors, but it is also hard work to prepare a Getter/Setter for each one. There are macros for this purpose, but even so, a large number of macros reduces readability. This is especially true when using From<T> Trait.

So how can this be simplified? It is the technique of “converting all fields to public”.

This allows for a simplified representation, as in the following example

pub struct Book {
    id: u64,
    name: String,
    stocked_at: String,
    author: String,
    // ... too many fields...
}

impl Book {
    pub fn into_destruct(self) -> DestructBook {
        DestructBook {
            id: self.id,
            name: self.name,
            stocked_at: self.stocked_at,
            author: self.author,
        }
    }
}

pub struct DestructBook {
    pub id: u64,
    pub name: String,
    pub stocked_at: String,
    pub author: String,
    // ... too many fields (All `public`.)...
}

fn main() {
    let book = Book {
        id: 1234_5678_9999_0000u64,
        name: "name".to_string(),
        stocked_at: "2023/01/03".to_string(),
        author: "author".to_string()
    };
    
    let des = book.into_destruct();

    println!("{:?}", des.id);
}

There are several problems with this method, the most serious of which is the increase in boilerplate.
Using the multi-cursor feature of the editor, this can be done by copy-pasting, but it is still a hassle.

Therefore, I created a Procedural Macro that automatically generates structures and methods:

use destructure::Destructure;

#[derive(Destructure)]
pub struct Book {
    id: u64,
    name: String,
    stocked_at: String,
    author: String,
    // ... too many fields...
}

fn main() {
    let book = Book {
        id: 1234_5678_9999_0000u64,
        name: "name".to_string(),
        stocked_at: "2023/01/03".to_string(),
        author: "author".to_string()
    };

    // Auto generate
    let des: DestructBook = book.into_destruct();

    println!("{:?}", des.id);
}

You can also perform safe value substitution by using reconstruct(), which performs the same role as the following usage.

use destructure::Destructure;

#[derive(Debug, Eq, PartialEq, Clone, Destructure)]
pub struct Book {
    id: u64,
    name: String,
    stocked_at: String,
    author: String,
    // ... too many fields...
}

fn main() {
   let before = Book {
       id: 1234_5678_9999_0000u64,
       name: "name".to_string(),
       stocked_at: "2023/01/03".to_string(),
       author: "author".to_string()
   };

   let author = "After".to_string();

   // Auto generate
   let after = before.clone().reconstruct(|before| {
       before.author = author;
   });

   assert_ne!(before, after);
}

Derive Macros

  • Automatically implements into_destruct() and freeze() methods.
  • Automatically implements substitute() methods.