Essential insights from Hacker News discussions

Subsecond: A runtime hotpatching engine for Rust hot-reloading

Here's a breakdown of the key themes from the Hacker News discussion about the "subsecond" hotpatching tool for Rust, along with direct quotes to support each point:

Excitement and Potential Use Cases

Many users expressed initial excitement about the possibilities of subsecond, particularly for speeding up development cycles in Rust.

  • mmastrac: "I'll have to give this a shot for some of the Rust server work I'm doing. progscrape.com uses a lot of tricks to boot quickly specifically because of the edit-compile-run cycle being slow (mostly deferred loading of indexes, etc)." He also sees potential at his day job: "My current day job @ Gel has me working on a Rust socket frontend for some pretty complex code and that could also be pretty interesting."
  • weinzierl: "Very nice. For a long time I wondered who would use hotpatching but working with large Java applications made me appreciate the possibility even if it is not 100% reliable (as it is in Java)."
  • csomar: "This is less worse than it sounds. The stuff I need hot reloading for is less than 5% of the total code base. It is usually stuff that I can't debug (ie: API response). So I am back and forth re-compiling. If I can get hot-reloading for that, that's a 95% time improvement for me. I could live with re-compiling for the rest of the code."

Subsecond's Implementation and Mechanism

The creator, jkelleyrtp, explained the technical details behind subsecond:

  • jkelleyrtp: "The gist of it is that we intercept the Rust linking phase and then drive rustc manually. There's some diffing logic that compares assembly between compiles and then a linking phase where we patch symbols against the running process. Works across macOS, Windows, Linux, iOS, Android, and WASM. On my m4 I can get 130ms compile-patch times, quite wicked stuff."
  • jkelleyrtp: "We handle the hard parts that the traditional dylib-reloading doesn't including TLS, statics, constructors, etc."

The subsecond::call Wrapper: A Source of Concern and Clarification

A significant portion of the discussion revolved around the requirement of wrapping code with subsecond::call, with some users initially viewing it as cumbersome. However, the creator clarified the intended usage:

  • modeless: "Interesting, but the documentation makes it sound like you have to preemptively wrap all the code you think you might want to change in a special wrapper "call" function. If true that makes this a lot less appealing than existing solutions for other languages that can modify any function without special annotations."
  • jkelleyrtp: "You basically need to wrap your program's tick() function. Otherwise you might be in the middle of malloc, hot-patch, and your struct's layout and alignment changes, and your program crashes due to undefined behavior."
  • anderskaseorg: "There is a well-defined reload point—it’s the subsecond::call wrapper around tick(). But the hypothetical design that you seem to have in mind where this doesn’t exist would not have a well-defined reload point, so it would need to be able to preempt your program anywhere."
  • jkelleyrtp: "The goal is that frameworks just bake subsecond::current into their tick() function and end-users get hot-patching for free."
  • jkelleyrtp: "Creator here - you only need one subsecond::call to hook into the runtime and it doesn't even need to be in your code - it can be inside a dependency."

The creator emphasizes that the goal is for frameworks to integrate subsecond::call so application developers can benefit without needing to manually wrap their code. The focus on wrapping the tick() function is linked to concerns about program state during hotpatching.

Trade-offs and Design Choices

Some commentators critiqued the design decisions, especially the need for the subsecond::call wrapper, arguing for alternative approaches like using dylibs.

  • prideout: "Neat but I would prefer simply using a dylib for the part of my code that I want to be reloadable."
  • jesse__: "Strong agree here. The 'purity' BS of not modifying the running programs address space appears to come at the cost of significant programmer pain-in-the-ass. Having to hand-hold the library to maintain the indirection table is a hard no for me."
  • jkelleyrtp: "I wouldn't say it's purity - the first version of subsecond actually did do in-process modification - but after playing around with it for a while, I much preferred the light runtime integration. The dioxus integration was about 5 lines of code, so it's quite minimal." The creator justifies the design choices by citing the complexities introduced by allowing arbitrary modifications at any moment, particularly when dealing with dynamic memory allocation and other stateful operations. He highlights the goal of minimizing user integration effort, particularly within frameworks.

Framework Integration

The existing and planned integrations with frameworks like Dioxus, Bevy, Axum, Ratatui and egui were also highlighted as a significant advantage.

  • jkelleyrtp: "Currently Dioxus and Bevy have subsecond integration so they get automatic hot-patching without any end-user setup. / We hope to release some general purpose adapters for axum, ratatui, egui, etc."
  • cchance: "Wonder if the other web frameworks like leptos will adopt subsecond or adopt their own"

Limitations and Scope

The discussion touched on limitations, such as only allowing the main crate to be hotpatched, and the handling of struct layout changes.

  • mmastrac: "It does appear to have a limitation where it will only allow the main crate to be hotpatched. That's less than ideal, but I suppose the convenience might justify some code structure changes to allow that."
  • anderskaseorg: "Layout changes are supported for structs that don’t persist across the well-defined reload point."

Desire for Plugin Systems in Rust

A related desire for better plugin systems in Rust was also expressed.

  • written-beyond: "I really want rust dylibs to be a reality. A plugin system where a library can implement a specific versioned number of a trait and we can dynamically load in that implementation to get changed behaviour. Right now implementing anything like that requires a lot of unsafe which I'm not comfortable with."

Cross-Language Desires

Finally, a comment showed interest beyond Rust.

  • cyberax: "Can we have the same for Go, pretty please?"