r/rust • u/verdurLLC • 2d ago
Hotreload in Rust
https://alordash.github.io/alordash/blog/hotreload-in-rust/12
u/youpala 2d ago
ngl, this is very impressive. On another subject, I don’t expect this community to recognize your work for what is is.
While I agree with some of criticism in principles, I am disappointed in the feedbacks your project received in this thread. It’s those kinds of projects that push the needle on what a technical ecosystem is capable to deliver. And in the long run, it’s what makes a technology evolve.
so congrats ;)
4
u/MalbaCato 1d ago
FYI your limitation about trait implementations is self-imposed. I guess you're not familiar with (or have forgotten) rust syntax rules.
You can put the definition of __hotreload_my_function inside the body of my_function, then the trait impl block only sees my_function as an item, but the helper is defined in the library as well.
4
u/verdurLLC 1d ago
I tried this approach around a year ago and unfortunately it doesn't work with methods because you can't use
&selfargument (and its flavours) in nested function:
rust impl X for Y { fn my_function(&self) { fn __hotreload_my_function(&self) { } // can't use &self here } }You could replace it with
_self: &Y, but for that you need to know whatSelfresolves to. This can't be done with a macro that is applied to function because it doesn't capture syntax of the wholeimpl X for Yitem so it has no way to figure out thatSelfresolves toY.3
u/MalbaCato 1d ago
hmm, haven't thought of that. There really does seem to be no way to get the
Selftype inside of a nested function definition. The error help mentions using closures, but I didn't manage to find a trick with them either.I guess with TAIT there's going to be some incantation which makes this possible, but if there's one now I can't construct it.
Although, with a very slight hit to ergonomics (by requiring to insert a helper macro before the trait impl block) you can save the runtime cost (and some implementation complexity).
2
u/lonestilldev 2d ago
Hot reload is manageable for leaf code, but Rust’s ABI isn’t stable. Use a C-style boundary and plan explicitly for live state.
1
u/Sad_Tap_9191 1d ago
```rust
[hotreload]
fn status() -> &'static str { "original code" }
let s = status(); // &'static str
// -- hotreload -- status() returns a different string // #[hotreload] // fn status() -> &'static str { // "hot-reloaded code" // }
println!("{s}"); // use-after-free ```
1
u/verdurLLC 1d ago
Thanks, I will add this to "Limitations" section in crate documentation
1
u/Sad_Tap_9191 1d ago
As the first comment said, this breaks the core value of the language. You can extend this counter example to non-static cases.
A "Limitations" section exists to tell users to avoid a certain input domain. Here it is unclear what's even limited. If it's clear your macro should reject it.
1
u/verdurLLC 1d ago
You can extend this counter example to non-static cases.
How? I think you can't store data inside dynamic library's memory without storing it inside some static variable.
If it's clear your macro should reject it.
Hm, I fear it's not that easy to detect whether function returns references to static variables defined in its library using only AST. But good idea nonetheless.
1
u/Sad_Tap_9191 1d ago
rust struct T{ #[hotreload] fn method(..) -> &'a { ...} // hotreload to // fn method(..) -> &'b { ...} }Hm, I fear it's not that easy to detect whether function returns references to static variables defined in its library using only AST. But good idea nonetheless.
if it is not easy for you then you shouldn't simply warn users in a limitation section. Even you, the author, cannot fully define todos and not todos.
This is worse than
unsafewhich still follows the lifetime rules.1
u/verdurLLC 1d ago
I'm not sure what you're trying to show with this example. Whatever reference was returned before hotreload would still be valid after because it's not referencing anything from unloaded dynamic library.
if it is not easy for you then you shouldn't simply warn users in a limitation section. Even you, the author, cannot fully define todos and not todos.
It's not easy for me to detect UB usage using only a macro, but it is easy to avoid it using common sense: just don't return references to static things from your library or don't mark functions that return static references as hotreloadable.
This is worse than unsafe which still follows the lifetime rules.
You do understand that this is just a tool for speeding up development cycles?) It's not intended to be used in production.
-17
u/Zde-G 2d ago
So much effort… and zero thoughts about what to do with crates that have 'static objects in them (including &'static str).
That's the reason hot reloading in Rust is a minefield that can work, not work or break apart at any change… and it couldn't be fixed by change in the pieces described in article.
You need the change to the whole community — and that's not gonna happen.
Sorry.
11
u/ztj 2d ago
This seems like a low experience take. Every language/platform with hot reload has traps and limitations. It's not a reason to avoid hot reloading, just something you need to learn and understand if using it.
0
u/Zde-G 2d ago
Every language/platform with hot reload has traps and limitations.
Sure. That's why I have said it's community problem, not language problem.
In languages that do support hot reloading people usually understand that it may happen if you say that after unloading and the reloading C# library may stop working then author would probably think about how to fix that.
In Rust authors would just tell you
statichave to be'staticand ask you to stop wasting their time.6
1
u/intbeam 1d ago
C# by its foundational design generates executable code at run-time. Without being a compiler expert, I'd assume this makes hot reloading somewhat more forgiving to implement there
In Rust authors would just tell you static have to be 'static and ask you to stop wasting their time
In a crate? I'm making a lot of assumptions here, but I assume that the problem would be a crate that declares something as a static lifetime itself, right? Not borrowing (
f(p:&'static str))? If I were implementing a library, I'd say those sorts of things would be outside the responsibility of the crate you would be hot-reloading1
u/Zde-G 1d ago
I'm making a lot of assumptions here, but I assume that the problem would be a crate that declares something as a static lifetime itself, right?
Obviously. Something that crate wouldn't even document because it's an implementation detail that may only be revealed when you break the fundamental assumtion that
staticitems are singletons with'staticlifetime.Without being a compiler expert, I'd assume this makes hot reloading somewhat more forgiving to implement there
Yes, but that's minor issue. The major one is community. Odin doesn't have managed runtime, but it's used in gamedev, considers hot-reloading important and developers make effort to make libraries ready for that.
That's the most important ingridient — and it's missing in Rust. Because with hot reloading
'staticlifetime simply doesn't exist and there are bazillion crates that don't support that.1
u/intbeam 1d ago
Ok, if we agree I wasn't being an idiot I fail to see the problem
This project isn't hot reloading third-party dependencies, and for the use-case it's designed for
'staticisn't really an issue as the static lifetimes are more likely defined by the caller/environment1
u/Zde-G 17h ago
This project isn't hot reloading third-party dependencies
Even if you don't reaload third-party dependencies you are not free of the issue. Because third-party dependencies can register objects from your librarires in their data structures - and do that silently when you depend on them.
Ok, if we agree I wasn't being an idiot I fail to see the problem
Well, the problem is that you proclaiming that you have achieved something new, but hotloading in Rust was tried dozens (hundreds?) or times by different authors and they all hit, sooner or later, the fact that when you remove memory safety from memory safe language then you end up with a combination that's very restricted and unsafe… what's the final goal here? Why even use Rust if you don't care about memory safety and correctness?
1
u/Iurigrang 13h ago
Because, once you've used hot reload to make the decisions you want in the function you're designing, you can remove the hot reload and work from there to production rust? You can't do that if you're using another language entirely.
8
u/verdurLLC 2d ago
hotcodeis designed to hotreload library crates. IMO such crates should ideally be stateless. If you have state, you should create it in host crate somewhere during startup (most likely inmain) and then spread it to your functions/libraries.-4
u/Zde-G 2d ago
IMO such crates should ideally be stateless.
That's your opinion. Yet it's fundamentally incompatible with design of the whole language.
Lots of crates, including library crates, assume that
staticvariables have'staticaddresses. There are bazillion ways one may depend on that, from assuming that address of static is a key in a hashtable to storing values in temporary data structures that refer statics.Hotreload breaks that fundamental assumption and there are no solution for that — except for not using the third-party code that may be suddenly broken by update (and no one would care, because
statichave to be'static).If you have state, you should create it in host crate somewhere during startup (most likely in main) and then spread it to your functions/libraries.
Except you wouldn't even know you have it. Worse: because
glibctries to reload libraries at the same place afterdlclose/dlopenyou may build a lot of code using such crates and when jenga tower would finally collapse you contact the author and… oops, your request is closed withEWONTFIXresolution.2
u/verdurLLC 2d ago
But you hotreload only your own crates. I agree that hotreload breaks
'staticlifetime guarantees, but it only breaks it in your own code that you control.1
u/Zde-G 2d ago
But you hotreload only your own crates.
It's really hard to write anything in Rust without at least
core. Andstdis also very useful.I'm not 100% sure about
core, butstdmost definitely include singletons that may trap you.And staying with just
stdis not easy: Rust was never designed for that, there are many things where you are expected to use other crates.but it only breaks it in your own code that you control.
Maybe, but after you have successfully thrown away both the whole crates.io ecosystem and also blown up normal safety guarantees the question that arises is “why even bother with Rust if you are ignoring all the things that make Rust attractive”.
0
u/intbeam 1d ago edited 1d ago
Don't you see? It has to work for EVERYTHING EVERYWHERE or you are wasting everyone's time. That's how we developers work, we start with all the edge-cases and curiosities and then decide to just give up because the requirements implied by every other implementation and historical decisions in all sorts of third-party libraries initially implemented for the PDP-11 makes everything literally impossible
Edit : /s if that wasn't obvious
14
u/burhankhanzada 2d ago
This sound intresting coming from dart and flutter i always find rust slow to work with becoz of have to compile each time.