<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Kate Korsaro</title>
    <description>The latest articles on Forem by Kate Korsaro (@kate_korsaro).</description>
    <link>https://forem.com/kate_korsaro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3543500%2Fffaba829-8c77-4f02-ac56-d37d04fd842b.png</url>
      <title>Forem: Kate Korsaro</title>
      <link>https://forem.com/kate_korsaro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kate_korsaro"/>
    <language>en</language>
    <item>
      <title>Persisting data in Rust with Heave</title>
      <dc:creator>Kate Korsaro</dc:creator>
      <pubDate>Fri, 03 Oct 2025 13:18:05 +0000</pubDate>
      <link>https://forem.com/kate_korsaro/persisting-data-in-rust-with-heave-3h3d</link>
      <guid>https://forem.com/kate_korsaro/persisting-data-in-rust-with-heave-3h3d</guid>
      <description>&lt;p&gt;As a developer, I often find myself working on small personal projects where I need a quick and easy way to persist data. Whether it's a simple command-line tool or a small web service, the need for data persistence is a recurring theme. For these kinds of projects, setting up a full-fledged database with a rigid schema can feel like overkill. I wanted something that was lightweight, flexible, and allowed me to evolve my data structures without hassle.&lt;/p&gt;

&lt;p&gt;This led me to create &lt;code&gt;heave&lt;/code&gt;, a small Rust library designed for exactly this purpose. My primary goal was to have a way to dump data into a SQLite database with minimal friction. SQLite is a perfect fit for small projects: it's serverless, self-contained, and easy to embed within an application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Embracing Flexibility with the Entity-Attribute-Value (EAV) Model
&lt;/h2&gt;

&lt;p&gt;One of the core design decisions in &lt;code&gt;heave&lt;/code&gt; is the use of the Entity-Attribute-Value (EAV) model. If you're not familiar with EAV, it's a data model that represents data as a collection of three-part tuples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Entity:&lt;/strong&gt; The object you want to describe (e.g., a user, a product).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Attribute:&lt;/strong&gt; A property of the entity (e.g., "name", "price").&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Value:&lt;/strong&gt; The value of the attribute (e.g., "Alice", 9.99).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main advantage of the EAV model is its flexibility. You can add new attributes to entities without having to alter the underlying database schema. This is a huge win for projects where the data structure is likely to change over time. It allows for a more "fluid" approach to data modeling, which is exactly what I needed for my personal projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;EAV&lt;/code&gt; Trait: Bridging the Gap Between Structs and Entities
&lt;/h2&gt;

&lt;p&gt;To make the process of working with the EAV model in Rust as seamless as possible, &lt;code&gt;heave&lt;/code&gt; introduces the &lt;code&gt;EAV&lt;/code&gt; trait. This trait provides a bridge between your custom Rust structs and the EAV representation of your data.&lt;/p&gt;

&lt;p&gt;By implementing the &lt;code&gt;EAV&lt;/code&gt; trait for your structs, you can easily convert them into a "fluid entity" that can be persisted to the database. The trait requires you to define a &lt;code&gt;class&lt;/code&gt; for your struct, which is used to group entities of the same type in the database. For a type &lt;code&gt;T&lt;/code&gt; that implements &lt;code&gt;EAV&lt;/code&gt;, it's implied that &lt;code&gt;From&amp;lt;Entity&amp;gt; for T&lt;/code&gt; and &lt;code&gt;Into&amp;lt;Entity&amp;gt; for T&lt;/code&gt; (via &lt;code&gt;From&amp;lt;T&amp;gt; for Entity&lt;/code&gt;) are also implemented.&lt;/p&gt;

&lt;p&gt;Here's a conceptual example of how it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Your custom struct&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;in_stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Implement the EAV trait for your struct&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;EAV&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;'static&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"product"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've implemented the &lt;code&gt;EAV&lt;/code&gt; trait, you can use &lt;code&gt;heave&lt;/code&gt;'s persistence functions to save your &lt;code&gt;Product&lt;/code&gt; instances to the SQLite database. The library takes care of transforming the struct's fields into the EAV format and storing them in the appropriate tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Idiomatic Conversions with the &lt;code&gt;From&amp;lt;T&amp;gt;&lt;/code&gt; Trait
&lt;/h2&gt;

&lt;p&gt;To make the conversion process even more idiomatic and seamless for Rust developers, &lt;code&gt;heave&lt;/code&gt; leverages the standard &lt;code&gt;From&amp;lt;T&amp;gt;&lt;/code&gt; trait. This trait is the idiomatic way in Rust to handle conversions between different types. In &lt;code&gt;heave&lt;/code&gt;, it's used in two key ways to map your custom structs to and from &lt;code&gt;heave&lt;/code&gt;'s internal &lt;code&gt;Entity&lt;/code&gt; representation.&lt;/p&gt;

&lt;h3&gt;
  
  
  From Your Struct to a Heave Entity
&lt;/h3&gt;

&lt;p&gt;First, you implement &lt;code&gt;From&amp;lt;YourStruct&amp;gt; for Entity&lt;/code&gt;. This allows you to easily convert an instance of your data-carrying struct into a "fluid" &lt;code&gt;Entity&lt;/code&gt; that &lt;code&gt;heave&lt;/code&gt; can understand and persist. This conversion is typically where you would map your struct's fields to the attributes of the &lt;code&gt;Entity&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's a conceptual look at the implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;From&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Entity&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;new&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.with_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.with_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.with_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="py"&gt;.price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.with_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"in_stock"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="py"&gt;.in_stock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="py"&gt;.model&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="nf"&gt;.set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;entity&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this in place, you can simply use &lt;code&gt;.into()&lt;/code&gt; to perform the conversion in your code, making it clean and readable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;my_product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"prod-123"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Laptop"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PenguinX"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;in_stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_product&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  From a Heave Entity Back to Your Struct
&lt;/h3&gt;

&lt;p&gt;Conversely, you also implement &lt;code&gt;From&amp;lt;Entity&amp;gt; for YourStruct&lt;/code&gt;. This enables you to transform an &lt;code&gt;Entity&lt;/code&gt; loaded from the database back into your strongly-typed custom struct. This is where you'd map the attributes from the &lt;code&gt;Entity&lt;/code&gt; back to your struct's fields, handling potential type conversions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;From&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_opt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;in_stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"in_stock"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows for an equally straightforward conversion when retrieving data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Assume 'entity_from_db' is an Entity loaded from heave&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entity_from_db&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using the standard &lt;code&gt;From&amp;lt;T&amp;gt;&lt;/code&gt; trait for these mappings, &lt;code&gt;heave&lt;/code&gt; provides a familiar and ergonomic API that integrates smoothly into typical Rust code, reducing boilerplate and improving clarity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with Heave
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;heave&lt;/code&gt; is still in its early stages of development, but it already provides the core functionality for persisting and loading data using the EAV model. The library is designed to be simple and easy to integrate into your projects.&lt;/p&gt;

&lt;p&gt;If you're looking for a lightweight and flexible way to handle data persistence in your Rust projects, I encourage you to give &lt;code&gt;heave&lt;/code&gt; a try. I'm excited to see how it can help other developers who share the same need for a simple, no-fuss persistence solution.&lt;/p&gt;

&lt;p&gt;You can find the source code for &lt;code&gt;heave&lt;/code&gt; on GitHub at &lt;a href="https://github.com/katekorsaro/heave" rel="noopener noreferrer"&gt;https://github.com/katekorsaro/heave&lt;/a&gt;. Contributions are always welcome, whether it's bug reports, feature requests, or pull requests.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>database</category>
      <category>sqlite</category>
      <category>eav</category>
    </item>
  </channel>
</rss>
