Top
Best
New

Posted by ingve 12/21/2025

Rue: Higher level than Rust, lower level than Go(rue-lang.dev)
Related: https://steveklabnik.com/writing/thirteen-years-of-rust-and-...
257 points | 269 commentspage 3
dingdingdang 12/22/2025|
Any tentative ideas yet as to how you will manage the memory management? Sounds like a sort of magic 3rd way might be in the making/baking!
steveklabnik 12/22/2025|
Something in the area of linear types and mutable value semantics.
dingdingdang 12/22/2025||
Anything out there for reference or would you be implementing from theory/ideas here? God speed to you in terms of the project overall, it's exciting to see the beginnings of a rust-like-lang without the headaches!
steveklabnik 12/22/2025||
Not implemented yet, I’m reading papers :)

Thanks!

metaltyphoon 12/23/2025||
What was the rationale to not use cargo? By the way, I really enjoy when you are a guest on the fallthrough podcast.
steveklabnik 12/23/2025|
Thanks!

So, one reason is "I just want to learn more about buck2."

But, for the first iteration of Rue, I maintained both. However, for a language project, there's one reason Cargo isn't sufficient now, and one reason why it may not later: the first one is https://github.com/rue-language/rue/blob/trunk/crates/rue-co... : I need to make sure that, no matter what configuration I build the compiler in, I build a staticlib for the runtime. With Cargo, I couldn't figure out how to do this. In test mode, it would still try to build it as a dylib.

Later, well, the reason that rustc has to layer a build system on top of Cargo: bootstrapping. I'm not sure if Rue will ever be bootstrapped, but rustc uses x.py for this. Buck does it a lot nicer, IMHO https://github.com/dtolnay/buck2-rustc-bootstrap

homarp 12/22/2025||
In the intro text, the Ramsus is who ? a typo about php creator or a more obscure language creator?
steveklabnik 12/22/2025|
It's a typo, I'm referring to https://en.wikipedia.org/wiki/Rasmus_Lerdorf. I'll fix it, thank you :)
tete 12/22/2025||
Looks nice, but -> syntax always feels extremely off-putting. What does it get me?
steveklabnik 12/22/2025|
I’m just copying Rust here because I care more about semantics at the moment. I may get rid of it, see some previous musings around this here https://steveklabnik.com/writing/too-many-words-about-rusts-...
Panzerschrek 12/22/2025||
How does it achieve memory safety?
steveklabnik 12/22/2025|
Right now? By not even having heap allocation (though I'll be sending in the first PR for that soon.)

Eventually: through not having references, thanks to mutable value semantics. Also linear types.

But that's just ideas right now. It'll get there.

PunchyHamster 12/22/2025||

    fn fib(n: i32) -> i32
so unnecesary, just extra writing. We're developers, we understand that function return types are after first ()

       let mut i = 0;
No, no, copy Go here, i := 0 is just fine
norir 12/21/2025||
I wince every time I see naive recursive fibonacci as a code example. It is a major turnoff because it hints at a lack of experience with tail call optimization, which I consider a must have for a serious language.
stouset 12/21/2025||
Would someone please explain to me why TCO—seemingly alone amongst the gajillions of optimization passes performed by modern compilers—is so singularly important to some people?
oersted 12/21/2025|||
For people that like functional style and using recursion for everything, TCO is a must. Otherwise there’s no way around imperative loops if you want decent performance and not having to worry about the stack limit.

Perhaps calling it an “optimization” is misleading. Certainly it makes code faster, but more importantly it’s syntax sugar to translate recursion into loops.

int_19h 12/22/2025||
You don't need full fledged TCO for that; see Clojure's recur for an example. Zig recently added something similar but strongly typed with match/continue. These all map exactly to a closed set of mutually recursive functions with a single entry point, which is quite sufficient (and then some) to fully replace iterative loops while still desugaring to the same exact code.
oersted 12/22/2025||
Indeed there are more explicit versions of such mechanisms, which I prefer, otherwise there’s always a bit of paranoia about recursion without assurance that the compiler will handle it properly.
Rusky 12/21/2025||||
TCO is less of an optimization (which are typically best-effort on the part of the compiler) and more of an actual semantic change that expands the set of valid programs. It's like a new control flow construct that lives alongside `while` loops.
chuckadams 12/22/2025||||
When you have recursive data structures, it's nice when the algorithms have the same shape. TCO is also handy when you're writing fancy control flow operations and implement them with continuation-passing style.
aaronblohowiak 12/21/2025||||
functional programming background / SICP ?
zephen 12/22/2025|||
It virtue-signals that they're part of the hip functional crowd.

(To be fair, if you are programming functionally, it is essential. But to flat-out state that a language that doesn't support isn't "serious" is a bit rude, at best.)

mrkeen 12/22/2025||
Supporting recursion only to a depth of 1000 (or whatever) is equivalent to supporting loops of up to 1000 iterations.

If I put out a language that crashed after 1000 iterations of a loop, I'd welcome the rudeness.

Ar-Curunir 12/22/2025||
Plenty of languages, including very serious ones like C and Rust, have bounded recursion depth.
mrkeen 12/22/2025|||
Then let me rephrase:

If every iteration of a while-loop cost you a whole stack frame, then I'd be very rude about that language.

This works, btw:

  #include <stdio.h>

  long calc_sum(int n, long acc) {
    return n == 0
      ? acc
      : calc_sum(n-1, acc+n);
  }

  int main(void) {
    int iters = 2000000;
    printf("Sum 1...%d = %ld\n", iters, calc_sum(iters, 0));
    return 0;
  }
zephen 12/22/2025||
> If every iteration of a while-loop cost you a whole stack frame, then I'd be very rude about that language.

Well, sure, but real programmers know how to do while loops without invoking a function call.

steveklabnik 12/21/2025|||
I only have basic constant folding yet in terms of optimizations, but I'm very aware of TCO. I haven't decided if I want to require an annotation to guarantee it like Rust is going to.
int_19h 12/22/2025||
Please require some form of annotation like an explicit `tailcall` operator or something similar. TCO wrecks havoc on actionable backtraces, so it should be opt-in rather than opt-out.
steveklabnik 12/22/2025||
I am very sympathetic to this, for sure.
dullcrisp 12/22/2025|||
Plus we all know that fibs = 1 : 1 : zipWith (+) fibs (tail fibs) is the only serious Fibonacci implementation.
xpe 12/22/2025||
Who thunk of that one?
airstrike 12/21/2025||
"Well you can judge the whole world on the sparkle that you think it lacks.

Yes, you can stare into the abyss, but it's staring right back"

mrkeen 12/22/2025||
Please, it supports a hole at best. Maybe a pit. No way will this let you construct an abyss.
frizlab 12/21/2025||
How does this compare to Swift?
steveklabnik 12/21/2025|
I don't plan on implementing ARC, I don't think. I do think Swift/Hylo mutable value semantics is a neat idea that I do want to play around with.
gethly 12/22/2025||
This is a bit silly but when i look at new languages coming up I always look at the syntax, which is usually horrible(Zig and Rust are good examples), and how much garbage there is. As someone that writes in Go, I can't stand semicolons and other crap that just pollutes the code and wastes time and space to write for absolutely no good reason whatsoever. And as this compares itself with Go, I just cannot but laugh when I see ";", "->" or ":" in the example. At least the semicolon seems optional. But still, it's an instant nope for me.
mrkeen 12/22/2025||
Weird, that's exactly how I feel reading Go:

  func (lst *List[T]) Push(v T) {
    if lst.tail == nil {
        lst.head = &element[T]{val: v}
        lst.tail = lst.head
    } else {
        lst.tail.next = &element[T]{val: v}
        lst.tail = lst.tail.next
    }
}

And this one doesn't even have the infamous error-checking.

hu3 12/23/2025||
You cherry picked a contrived example but that's one of the cleanest generics implementations.

Now imagine if it had semicolons, ->, ! and '.

mrkeen 12/23/2025||
> You cherry picked a contrived example

List.add is contrived? What are you doing that's simpler the list.add?

> but that's one of the cleanest generics implementations.

You're saying it's typically worse than this?

gethly 12/23/2025||
He is referring to

  &element[T]{val: v}
the & is a pointer, which is common across most languages, but the [T] is a dynamic type T. Otherwise it would be just

  &element{val: v}
He says that element[T] is a clean/simple implementation of generics.
steveklabnik 12/22/2025|||
I respect your preferences! I like punctuation.

Even though I have a Perl tattoo, it'll never get like that, though.

(Semicolon rules, for now at least, will be the same as Rust)

imbnwa 12/22/2025||
What would be better?
gethly 12/22/2025|||
Remove all of that noise.

Take this:

  fn fib(n: i32) -> i32 {}
The (n: i32) can be just (n i32), because there is no benefit to adding the colon there.

The -> i32 can also be just i32 because, again, the -> serves no purpose in function/method definition syntax.

So you end up with simple and clean fn fib(n i32) i32 {}

And semicolons are an ancient relic that has been passed on to new languages for 80 fucking years without any good reason. We have modern lexers/tokenizers and compilers that can handle if you don't put a stupid ; at the end of every single effing line.

Just go and count how many of these useless characters are in your codebase and imagine how many keystrokes, compilation errors and wasted time it cost you, whilst providing zero value in return.

exceptione 12/22/2025|||
In a Hindley-Milner (or deriative) type system, types doesn't have to be explicit, making the number of arguments ambiguous here:

  fn fib(n i32) i32 {}
But even if they need to be written explicitly, type applications like `List a` would require syntax to disambiguate them.

Personally, I would like a language that pushes the programmer to write the types as part of a doc comment.

Also think about returning lambda's. Should it look like this?

  fn foo(n i32) (i32 i32) {}
Of course the IDE could help by showing the typographic arrows and other delineations, but as plaintext this is completely unreadable.

  > And semicolons are an ancient relic that has been passed on to new languages for 80 fucking years without any good reason. 
You still have to think about stuff like currying. You either delimit the line, or you use significant white space.
gethly 12/22/2025||
> Also think about returning lambda's. Should it look like this? > > fn foo(n i32) (i32 i32) {}

It should be

  fn foo(n i32, m i32) (i32, i32) {}
It will also allow future implementation of named returns, like in Go:

  fn foo(n i32) (a i32, b i32) {}
As for semicolon, that is needed only if you have inline expression:

  for (;;;) {}
Or inline block, like in Go:

  if foo := a + b; foo > c {}
exceptione 12/22/2025||

  > fn foo(n i32, m i32) (i32, i32) {}
But now consider returning a function with type¹

  Foo<T<string, T2>> -> (bool -> IDictionary<string, T3> -> i32 -> T3) where T2 : T3
even if you leave out the latter type constraint, I think it is hard to avoid undecidable ambiguity.

  fn foo(n i32, m T2) (????) {}

You quickly get ambiguity due to type parameters / generics, functions as arguments, and tuples if you don't syntactically separate them.

Even if you your context-depended parser can recognize it, does the user? I agree that a language designer should minimize the amount of muscle damage, but he shouldn't forget that readability is perhaps even more critical.

____

1. Note, even if the parser can recognize this, for humans the '>' is confusing unless syntax highlighting takes care of it. One time it delimits a generic type argument, the other time it is part of '->'. This is also an argument for rendering these things as ligatures.

gethly 12/22/2025||
This is pointless discussion as Go has all of these things already implemented, so there is no point in going backwards.
exceptione 12/22/2025||
The point is that a (new) syntax for any language needs to support any such implementation. The language implementation itself is not the point.
gethly 12/23/2025||
Yes, and I am saying Go has already solved all of this and it makes little sense to deviate too much from its syntax.
Joker_vD 12/22/2025||||
> The (n: i32) can be just (n i32), because there is no benefit to adding the colon there.

> The -> i32 can also be just i32 because, again, the -> serves no purpose in function/method definition syntax.

Well, there is, but it's more of a personal trait than a universal truth. Some human programmers (e.g. me) tend to read and parse (and even write, to some extent) source code more accurately when there is a sprinkle of punctuation thrown in into a long chain of nothing but identifiers and subtly nested parentheses. Some, e.g. you, don't need such assistance and find it annoying and frivolous.

Unfortunately, since we don't store the source code of our programs as binary AST blobs that could be rendered in a personalized matter, but as plain text instead, we have to accept the language designer's choices. Perhaps it actually has better consequences than the alternative; perhaps not.

gethly 12/22/2025||
the only reason why one might hold such an opinion is the lack of syntax highlighting.
elygre 12/22/2025|||
What’s with all the periods, one at the end of each paragraph? Fully wasted.
christophilus 12/22/2025|||
I wish more languages would adopt Clojure’s approach to optional delimiters in collections.

[2 45 78]

It’s just a nicer thing to view and type in my experience.

Regarding syntax soup, I think Odin is probably syntactically the cleanest of the lower level languages I’ve played with.

gethly 12/22/2025|||
oh, yeah. that looks good. i always hated using ", " delimiter for lists and the amount of typos it always takes to make clean(well, not with Go fmt).

Odin seems interesting but for me it has two deal-breakers: first one use the use of ^ for pointer de/reference. Not that it does not make sense, it's just that it is not an easy key to get to on my keyboard layout and i will not be changing that. The & and * are well known characters for this purpose and, at least for me, easily accessible on the keyboard. Second issue is the need to download many gigabytes of visual studio nonsense just so i am able to compile a program. Coming from Go, this is just a non-starter. Thirdly, and this is more about the type of work i do than the language, there are/were no db drivers, no http/s stack and other things i would need for my daily work. Other than that, Odin is interesting. Though I am not sure how I would fare without OOP after so many years with inheritance OOP and encapsulated OOP.

int_19h 12/22/2025|||
It's a Lisp thing, obviously, but also there's a benefit to explicit delimiters - it makes it possible to have an expression as an element without wrapping that in its own brackets, as S-exprs require.
catlover76 12/22/2025|
[dead]