<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Typing Theory]]></title><description><![CDATA[A blog about code, or whatever tickles my fancy]]></description><link>https://blog.typingtheory.com/</link><image><url>https://blog.typingtheory.com/favicon.png</url><title>Typing Theory</title><link>https://blog.typingtheory.com/</link></image><generator>Ghost 3.4</generator><lastBuildDate>Tue, 10 Mar 2026 03:07:06 GMT</lastBuildDate><atom:link href="https://blog.typingtheory.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Announcing Shredder 0.2.0!]]></title><description><![CDATA[<h2 id="introduction">Introduction</h2><p>I'm happy to announce a new release of <code>shredder</code>! In case you don't know, <code>shredder</code> is a garbage collection library for Rust. It's safe, easy to use, and concurrency friendly. Here is a small example of it in action:</p><pre><code class="language-rust">use std::cell::RefCell;

use shredder::{
    number_of_active_handles,</code></pre>]]></description><link>https://blog.typingtheory.com/announcing-shredder-0-2-0/</link><guid isPermaLink="false">603b588f3529dd04acaf8e25</guid><dc:creator><![CDATA[Others]]></dc:creator><pubDate>Fri, 13 Aug 2021 23:23:22 GMT</pubDate><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2><p>I'm happy to announce a new release of <code>shredder</code>! In case you don't know, <code>shredder</code> is a garbage collection library for Rust. It's safe, easy to use, and concurrency friendly. Here is a small example of it in action:</p><pre><code class="language-rust">use std::cell::RefCell;

use shredder::{
    number_of_active_handles, 
    number_of_tracked_allocations, 
    run_with_gc_cleanup, 
    Gc, Scan,
};

#[derive(Scan)]
struct Node {
    data: String,
    directed_edges: Vec&lt;Gc&lt;RefCell&lt;Node&gt;&gt;&gt;,
}

fn main() {
    // Using `run_with_gc_cleanup` is good practice, since it helps ensure destructors are run 
    // (but this is not required for correctness)
    run_with_gc_cleanup(|| {
        let a = Gc::new(RefCell::new(Node {
            data: "A".to_string(),
            directed_edges: Vec::new(),
        }));

        let b = Gc::new(RefCell::new(Node {
            data: "B".to_string(),
            directed_edges: Vec::new(),
        }));

        // Usually would need `get` for `Gc` data, but `RefCell` is a special case
        a.borrow_mut().directed_edges.push(b.clone());
        b.borrow_mut().directed_edges.push(a);
        // We now have cyclical data!
    });
    // Everything was cleaned up!
    assert_eq!(number_of_tracked_allocations(), 0);
    assert_eq!(number_of_active_handles(), 0);
}
</code></pre><p>And you can read more on <a href="https://github.com/Others/shredder">Github</a> or in my previous <a href="https://blog.typingtheory.com/shredder-garbage-collection-as-a-library-for-rust/">blog post</a>!</p><p>While most of the feature work for this was done a while ago, I'm bad at actually writing notes and releasing things, so this announcement/crates.io release is a bit delayed.</p><h2 id="new-features">New Features</h2><h3 id="trait-system-refactor-others-">Trait System Refactor (@Others)</h3><p>The main goal of this release was to enable some sort of Deref support. After thinking on this for a while, I came to the conclusion this was impossible with the original trait design. In 0.1, there were two critical traits: <code>GcSafe</code> and <code>Scan</code>. <code>GcSafe</code> captured every invariant required for data to be stored in a <code>Gc</code>. But in 0.2, with multiple smart pointers, that design falls apart.</p><p>That means that we needed to break down into more marker traits. Now in 0.2 we have:</p><ul><li><code>GcSafe</code> = captures being <code>Send + 'static</code>, or enough like <code>Send + 'static</code> to be safely scanned and managed in the background with exclusive access</li><li><code>GcDrop</code> = can safely run the destructor of this data in the background (allowable to access this data in a <code>Drop</code> implementation run by the collector)</li><li><code>GcDeref</code> = <code>Sync</code> + can be safely stored in a <code>DerefGc</code> (see below)</li><li><code>Scan</code> = captures the scanning logic, as previously</li></ul><p>These are of course imprecise definitions of unsafe traits--if you're curious, the exact rules for implementing these can be found in the <a href="https://docs.rs/shredder/0.2.0/shredder/">crate documentation</a>.</p><h3 id="derefgc-others-">DerefGc (@Others)</h3><p>Now for the most exciting new feature! The new smart pointer <code>DerefGc</code>. </p><p>First, a discussion of the issues with deref in the context of <code>shredder</code>. <code>shredder</code> lets you store <code>!Sync</code> data in <code>Gc</code>, so it's not possible to simply implement <code>Deref</code> for <code>Gc</code> (since data is scanned in the background). Further complicating this is the issue of <code>Drop</code> and interior mutability. <code>shredder</code> being able to run <code>Drop</code> implementations in the background is only possible due to the <code>get</code> guard we use to prevent access of data inside a dead <code>Gc</code>. This guard is also needed to prevent interior mutability being used to modify the object graph while the collector is running.</p><p>All that said, it is still possible to implement a <code>DerefGc</code> type. However we need to set the following restrictions:</p><ol><li>The data stored in <code>DerefGc</code> must be <code>Sync</code> </li><li>We can't naively run the <code>Drop</code> implementation of the interior data, or access the <code>DerefGc</code> in any <code>Drop</code> implementation run by the collector</li><li>The data inside the <code>DerefGc</code> must be immutable through <code>&amp;T</code> (no interior mutability), except if that interior mutable data is nested in a <code>Gc</code></li></ol><p>To assure these conditions, we utilize the new trait system. <code>DerefGc</code> is <code>!GcDrop</code>, assuring (2). Then, for any <code>T</code> in <code>DerefGc&lt;T&gt;</code>, <code>T</code> must be <code>GcDeref</code>, a new trait capturing condition (1) and (3).</p><p>While these restrictions (especially 3) are strict, it still leaves us with as useful a <code>Deref</code> type as I believe is possible with <code>shredder</code>'s current restrictions. The nice thing is that accessing data inside a <code>DerefGc</code> can never block (unlike calling <code>get</code> on a regular <code>Gc</code>)</p><h3 id="atomicgc-others-">AtomicGc (@Others)</h3><p>Along with enabling <code>DerefGc</code>, implementing the new trait system also gave me the tools to implement an atomic version of <code>Gc</code>. This is still pretty experimental, but it is pretty similar to crossbeams <code>Atomic</code> type (but with completely different performance characteristics). This type is <code>!GcDrop</code> for similar reasons to <code>DerefGc</code>. </p><p>This feature is pretty raw, and I'm pushing it for experimentation. It needs a bit more performance optimization to be truly useful.</p><h3 id="finalize-others-">Finalize (@Others)</h3><p>With these new smart pointer types, just using <code>Drop</code> is no longer good enough for <code>shredder</code>. While <code>Gc</code> itself will continue to default to <code>Drop</code>ing things, many new types are <code>!GcDrop</code>. Thus, like all gc crates before us, we are introducing a <code>Finalize</code> trait. </p><p>This is quite similar to <code>Drop</code>, but unsafe to implement, and with many restrictions to prevent unsoundness. (see crate documentation) This is what you'll need for cleanup logic for <code>DerefGc&lt;T&gt;</code>. <code>Gc</code> also has support if you'd prefer not to use <code>Drop</code>.</p><h3 id="derive-improvements-others-">Derive Improvements (@Others)</h3><p>The <code>Scan</code> derive has been updated to support enums, and implementing the new traits described above. <code>GcDrop</code> is opt-out, and <code>GcDeref</code> is opt-in. (This is for backwards compatibility reasons.) Please read the crate documentation for details.</p><p>Also in this release is a new <code>Finalize</code> derive, which eases implementation of <code>Finalize</code> for structs with finalizable fields.</p><p>Both derives also now support enums.</p><h3 id="scan-for-fxx-bool-and-btreemap-and-btreeset-alekratz-drgabble-">Scan for fXX, bool and BTreeMap and BTreeSet (@alekratz, @DrGabble)</h3><p>In the realm of "this shows someone else has run this code." Some new types got <code>Scan</code> implementations in this release! There are tons more standard library types that need traits implemented, and these are easy contributions if you want to get started with the codebase.</p><h3 id="unsized-types-alekratz-">Unsized Types (@alekratz)</h3><p>In the most sizable contribution from another human so far, @alekratz contributed support for storing unsized types in <code>Gc</code>s!  Since <code>CoerceUnsized</code> is nightly only (with no obvious route to stabilization?), this feature is a lot more useful if you are on the dark side, where you can now store whatever unsized type you want in a <code>Gc</code>:</p><pre><code class="language-rust">let data: Gc&lt;dyn Debug&gt; = Gc::new(0u32);</code></pre><p>That being said, we also have janky stable support using the <code>ToScan</code> trait. This comes with a few asterisks. In order to get a <code>Gc&lt;dyn D&gt;</code> on stable you basically need to control the definition of <code>D</code> and your data needs to be <code>'static</code>. Until we have better ways of working with unsized data on stable, or someone contributes a cleaner hack, that is.</p><h3 id="downcast-alekratz-others-">Downcast (@alekratz, @Others)</h3><p>Along with being able to store trait objects, you also can now downcast them! While it's easy to forget the <code>Any</code> trait, for some applications a level of dynamic reflection is a must. With <code>Gc</code> now supporting unsized types and downcasting, there is definitely some cool interpreter to be built here!</p><h3 id="collector-refactoring-others-">Collector Refactoring (@Others)</h3><p>As part of onboarding other contributors to the codebase, the core collector code was refactored and better documented. This should helpfully make it easier to read and work on this slightly thorny code. (Which is ultimately just a simple mark and sweep collector, with some Rust specific tweaks.)</p><p>Also as part of this work, the concurrent HashMaps we were using behind the scenes were removed. They were replaced with a nifty chunked-linked-list plus <code>arc-swap</code> combo. Huge shoutout to the <code>arc-swap</code> crate, which is a beautiful  piece of engineering, and also indirectly powers the <code>DerefGc</code> mentioned above.</p><h3 id="soundness-fixes-others-">Soundness Fixes (@Others)</h3><p>Also while working in the collector, Inoticed a subtle soundness hole in the collector. The issue was related to how garbage is destructed in the background. Here is a diagram:</p><figure class="kg-card kg-image-card"><img src="https://blog.typingtheory.com/content/images/2021/03/image.png" class="kg-image"></figure><p>The essential problem is that destructors for garbage could hold handles to other garbage data (which would be otherwise unreachable), and send them to other threads. The solution is to mark all garbage as "dead" before running any destructors. (Which is slightly less efficient, but sound.) This allows "zombie" <code>Gc</code>s to exist, but since dead <code>Gc</code>s can't be turned into references, there is no soundness hole.</p><h2 id="the-future-0-3-0-">The Future (0.3.0?)</h2><p>In the future, I hope to spend some more time optimizing the crate. While both memory usage and CPU usage have been vastly reduced since initial versions, I have more ideas to try and optimize, and I don't want to leave performance on the table. ( I wanted to save this work until 0.2 has baked in the wild .) The two major optimizations are: (1) switching from tracking handles individually to simple handle counts, and (2) shortening the "stop the world" phase of collection with a more greedy approach.</p><p>I also feel <code>shredder</code> is reaching the point where a major limiting factor is lack of use cases. This supports my initial hypothesis that garbage collection is not needed in 99% of Rust programs. I do think there is room for me to experiment in places it could be useful. (For example, it'd be awesome to have a concurrent hash-map backed by <code>AtomicGc</code> rather than <code>crossbeam</code>'s epoch based reclamation, if only just to compare performance!) And of course if you have a use-case well served by garbage collection, please get in touch!</p><p>In terms of feature work, I'd quite like the crate to support <code>no-std</code> use-cases where <code>alloc</code> is available. While there is no fundamental reason why this should be impossible, the pervasive use of <code>rayon</code>, <code>parking-lot</code> and threading internally poses an issue. I think solving this properly requires writing a shim layer to abstract away the concurrent aspects from the collector implementation.</p><p>If you're interested in helping with this project (either to work on something mentioned in this post, or a smaller issue), feel free to have a look at the <a href="https://github.com/Others/shredder/issues">Github issues</a>, where I've documented what known issues and features need some love!</p>]]></content:encoded></item><item><title><![CDATA[Shredder: Garbage Collection as a Library for Rust]]></title><description><![CDATA[<p>I'm excited to announce <code>shredder</code>, a library for Rust that provides a garbage collected smart pointer, <code>Gc</code>. This smart pointer is useful in the same sorts of situations as <code>Rc</code>, but can handle reference cycles. It's available on <a href="https://crates.io/crates/shredder">crates.io</a> and <a href="https://github.com/Others/shredder">Github</a>. Here's how you could use it:</p><pre><code class="language-Rust">use std:</code></pre>]]></description><link>https://blog.typingtheory.com/shredder-garbage-collection-as-a-library-for-rust/</link><guid isPermaLink="false">5eddc2e649cad840d286dabc</guid><dc:creator><![CDATA[Others]]></dc:creator><pubDate>Thu, 11 Jun 2020 04:41:48 GMT</pubDate><content:encoded><![CDATA[<p>I'm excited to announce <code>shredder</code>, a library for Rust that provides a garbage collected smart pointer, <code>Gc</code>. This smart pointer is useful in the same sorts of situations as <code>Rc</code>, but can handle reference cycles. It's available on <a href="https://crates.io/crates/shredder">crates.io</a> and <a href="https://github.com/Others/shredder">Github</a>. Here's how you could use it:</p><pre><code class="language-Rust">use std::cell::RefCell;

use shredder::{
    Gc, Scan,
    run_with_gc_cleanup,
    number_of_active_handles,
    number_of_tracked_allocations,
};

#[derive(Scan)]
struct Node {
    data: String,
    directed_edges: Vec&lt;Gc&lt;RefCell&lt;Node&gt;&gt;&gt;,
}

fn main() {
    // Using `run_with_gc_cleanup` is good practice
    // (it helps ensure destructors are run)
    run_with_gc_cleanup(|| {
        let a = Gc::new(RefCell::new(Node {
            data: "A".to_string(),
            directed_edges: Vec::new(),
        }));

        let b = Gc::new(RefCell::new(Node {
            data: "B".to_string(),
            directed_edges: Vec::new(),
        }));

        // Usually would need `get` for `Gc` data,
        // but `RefCell` is a special case
        a.borrow_mut().directed_edges.push(b.clone());
        b.borrow_mut().directed_edges.push(a);
        // We now have cyclical data!
    });
    // Everything was cleaned up!
    assert_eq!(number_of_tracked_allocations(), 0);
    assert_eq!(number_of_active_handles(), 0);
}</code></pre><p>In this example we had <code>#[derive(Scan)]</code> on our struct. By implementing this trait, we let <code>shredder</code> know how it can find what <code>Gc</code> handles this data contains. This is an essential part of the tracing garbage collection that <code>shredder</code> implements. After we've done that, using the smart pointer is basically just plug and play! </p><p>The only ergonomic issue to note is the need for guard objects. In <code>shredder</code>, you always need a guard object when accessing data inside a <code>Gc</code>. Usually you need to use <code>.get</code> to get a <code>GcGuard</code>. (Guarded access is key to implementing <code>shredder</code>'s guarantees in a performant way.) Now, notice how we didn't have to do this in the above example. In general, <code>Gc</code> is only useful with interior mutability, so <code>Gc&lt;RefCell&lt;?&gt;&gt;</code>, <code>Gc&lt;Mutex&lt;?&gt;&gt;</code> and <code>Gc&lt;RwLock&lt;?&gt;&gt;</code> have special methods that let you skip this step (see documentation for more details).</p><p>People have experimented with garbage collection in Rust before, but I wasn't happy with existing libraries. I do not believe there is an existing Rust library providing an easy to use garbage collection solution that can robustly work in concurrent programs, as well as handle non-<code>'static</code> data.  <code>shredder</code> does that and more. Let me break down what I consider the best features of <code>shredder</code>. (If you're interested in a deep dive into how it all works, I hope to provide a follow post on the design.)</p><p><strong>1 Safety First)</strong> It should be impossible to cause unsafe behavior in safe code using <code>shredder</code>. The only unsafe parts of the API are writing manual implementations of the core traits: <code>Scan</code>, <code>GcSafe</code> and <code>Finalize</code>. I've done my best to ensure they have complete documentation explaining how to implement them safely.</p><p><strong>2 Ergonomics Second)</strong> I believe a lot of the existing solutions are fairly hard to grok, requiring complex macros or weird setups with lifetimes. In <code>shredder</code>, <code>Gc</code> is basically just a fancy <code>Arc</code>, and I hope that makes the library easier to use. (And in the future I even want to make the guard object optional, assuming I can make it safe and performant!)</p><p><strong>3 Ready for Fearless Concurrency)</strong> <code>shredder</code> was always designed to deal with multithreaded programs. You can send a <code>Gc</code> around just as if it was an <code>Arc</code>, and I think that's really cool! Keeping with this, I've designed the collector so that even if a thread holds onto a ton of <code>GcGuard</code>s, collection can still proceed as normal. The only operation that should implicitly block on collection in <code>shredder</code> is calling <code>.get</code> (or one of the helper methods wrapping <code>.get</code>). </p><p><strong>4 Seamless Destruction) </strong>By default, <code>Gc::new</code> only takes <code>'static</code> data, and when it is found to be unreachable, it is dropped and its destructor is called. (There are safety checks in place to prevent you from exploiting cyclical data in the destructor.) This saves users the pain of having to deal with <code>Finalize</code> in the common case. The <code>run_with_gc_cleanup</code> method is also provided, and helps ensure destructors are run at the end of your program.</p><p><strong>5 Clean Finalization) </strong>Sometimes, however, you need non-<code>'static</code> data. Unfortunately, since we may scan data after lifetimes have expired, you cannot use raw references. Instead you must replace <code>&amp;'a T</code> with <code>R&lt;'a, T&gt;</code> and <code>&amp;'a mut T</code> with <code>RMut&lt;'a, T&gt;</code>. Outside of <code>Scan</code> and <code>Finalize</code> implementations, these can be used as if they were regular references. Speaking of <code>Finalize</code>, if you implement it, and use <code>Gc::new_with_finalizer</code>, then you can use unsafe code to define code to be run right before your data is deallocated. </p><p><strong>6 Concurrent Collection and Destruction) </strong>Finally, as a nice side effect of <code>shredder</code>s design, automatic collection always happens in a background thread, and hopefully doesn't implement your regular processing too badly. Same goes for running destructors, which might actually be a performance win if your destructors are expensive.</p><p><code>shredder</code> isn't perfect. It needs a lot more performance optimization, and I think the background collection can become even more concurrent in the future. It also has a non-trivial memory overhead. And of course it is very new and immature.</p><p>However, I think that's okay for now. I designed <code>shredder</code> to be another tool in the Rust programmer's toolbox. <code>Gc</code> is a very extreme escape hatch that lets you escape usual ownership rules. It's something that should be used carefully in small doses, where giving up the superior performance of RAII memory management is worth it. (Perhaps for user provided data with unpredictable loops, or interestingly shaped graphs.) I'm excited to see what people can do with it! </p><p>It's available on <a href="https://crates.io/crates/shredder">crates.io</a> and <a href="https://github.com/Others/shredder">Github</a>. (If you've read this far, maybe you're interested in contributing? I've put some issues on the Github, and that might be a good place to start!)</p>]]></content:encoded></item></channel></rss>