I have mostly been writing Rust in the last 10 years, but recently (1 year) I have been writing Go as well as Rust.<p>The typical Go story is to use a bunch of auto generation, so a small change quickly blows up as all of the auto generate code is checked into git. Like easily a 20x blowup.<p>Rust on the other hand probably does much more such code generation (build.rs for stuff like bindgen, macros for stuff like serde, and monomorphized generics for basically everything). But all of this code is never checked into git (with the exception of some build.rs tools which can be configured to run as commands as well), or at least 99% of the time it's not.<p>This difference has impact on the developer story. In go land, you need to manually invoke the auto generator and it's easy to forget until CI reminds you. The auto generator is usually quite slow, and probably has much less caching smartness than the Rust people have figured out.<p>In Rust land, the auto generation can, worst case, run at every build, best case the many cache systems take care of it (cargo level, rustc level). But still, everyone who does a git pull has to re-run this, while with the auto generation one can theoretically only have the folks run it who actually made changes that changed the auto generated code, everyone else gets it via git pull.<p>So in Go, your IDE is ready to go immediately after git pull and doesn't have to compile a tree of hundreds of dependencies. Go IDEs and compilers are so fast, it's almost like cheating from Rust POV. Rust IDEs are not as fast at all even if everything is cached, and in the worst case you have to wait a long long time.<p>On the other hand, these auto generation tools in Go are only somewhat standardized, you don't have a central tool that takes care of things (or at least I'm not aware of it). In Rust land, cargo creates some level of standardization.<p>You can always look at the auto generated Go code and understand it, while Rust's auto generated code usually is not IDE inspectable and needs special tools for access (except for the build.rs generated stuff which is usually put inside the target directory).<p>I wonder how a language that is designed from scratch would approach auto generation.
I always thought of Go as low level and Rust as high level. Go has a lot of verbosity as a "better C" with GC. Rust has low level control but many functional inspired abstractions. Just try writing iteration or error handling in either one to see.
I wonder if it's useful to think of this as go is low type-system-complexity and rust is high type-system-complexity. Where type system complexity entails a tradeoff between the complexity of the language and how powerful the language is in allowing you to define abstractions.<p>As an independent axis from close to the underlying machine/far away from the underlying machine (whether virtual like wasm or real like a systemv x86_64 abi), which describes how closely the language lets you interact with the environment it runs in/how much it abstracts that environment away in order to provide abstractions.<p>Rust lives in high type system complexity and close to the underlying machine environment. Go is low type system complexity and (relative to rust) far from the underlying machine.
Rue author here, yeah I'm not the hugest fan of "low level vs high level" framing myself, because there are multiple valid ways of interpreting it. As you yourself demonstrate!<p>As some of the larger design decisions come into place, I'll find a better way of describing it. Mostly, I am not really trying to compete with C/C++/Rust on speed, but I'm not going to add a GC either. So I'm somewhere in there.
How very so humble of you to not mention being one of the primary authors behind TRPL book. Steve you're a gem to the world of computing. Always considered you the J. Kenji of the Rust world.
Seems like a great project let's see where it goes!
> Mostly, I am not really trying to compete with C/C++/Rust on speed, but I'm not going to add a GC either. So I'm somewhere in there.<p>Out of curiosity, how would you compare the goals of Rue with something like D[0] or one of the ML-based languages such as OCaml[1]?<p>EDIT:<p>This is a genuine language design question regarding an imperative/OOP or declarative/FP focus and is relevant to understanding the memory management philosophy expressed[2]:<p><pre><code> No garbage collector, no manual memory management. A work
in progress, though.
</code></pre>
0 - <a href="https://dlang.org/" rel="nofollow">https://dlang.org/</a><p>1 - <a href="https://ocaml.org/" rel="nofollow">https://ocaml.org/</a><p>2 - <a href="https://rue-lang.dev/" rel="nofollow">https://rue-lang.dev/</a>
Do you think you'll explore some of the same problem spaces as Rust? Lifetimes and async are both big pain points of Rust for me, so it'd be interesting to see a fresh approach to these problems.<p>I couldn't see how long-running memory is handled, is it handled similar to Rust?
Is this a simplified / distilled version of Rust ? Or Subset of Rust with some changes ?
Since it's framed as 'in between' Rust and Go, is it trying to target an intersection of both languages' use-cases?
I don't think you'd want to write an operating system in Rue. I may not include an "unsafe" concept, and will probably require a runtime. So that's some areas where Rust will make more sense.<p>As for Go... I dunno. Go has a strong vision around concurrency, and I just don't have one yet. We'll see.
Wow didn't realise it was you who was the author. I learnt a lot about Rust from your writings.
Yep. This was the biggest thing that turned me off Go. I ported the same little program (some text based operational transform code) to a bunch of languages - JS (+ typescript), C, rust, Go, python, etc. Then compared the experience. How were they to use? How long did the programs end up being? How fast did they run?<p>I did C and typescript first. At the time, my C implementation ran about 20x faster than typescript. But the typescript code was only 2/3rds as many lines and much easier to code up. (JS & TS have gotten much faster since then thanks to improvements in V8).<p>Rust was the best of all worlds - the code was small, simple and easy to code up like typescript. And it ran just as fast as C. Go was the worst - it was annoying to program (due to a lack of enums). It was horribly verbose. And it still ran slower than rust and C at runtime.<p>I understand why Go exists. But I can't think of any reason I'd ever use it.
C was designed as a high level language and stayed so for decades
> Memory Safe<p>> No garbage collector, no manual memory management. A work in progress, though.<p>I couldn't find an explanation in the docs or elsewhere <i>how</i> Rue approaches this.<p>If not GC, is it via:<p>a) ARC<p>b) Ownership (ala Rust)<p>c) some other way?
All the Rue code in the manual seems to also be valid Rust code, except for the @-prefixed intrinsics
Yes, I started off with the idea that Rue's syntax would be a strict subset of Rust's.<p>I may eventually diverge from this, but I like Rust's syntax overall, and I don't want to bikeshed syntax right now, I want to work on semantics + compiler internals. The core syntax of Rust is good enough right now.
Out of interest, what's the motivation? What are you hoping to do with Rue that Rust doesn't currently provide?
How is it a subset then if it has the @-prefix? Wait, does Rust's grammar still have the @ and ~ sigils from the pre 1.0 times for pointers?
Probably best to link to the repo itself, this is not meant to be used yet. <a href="https://github.com/rue-language/rue" rel="nofollow">https://github.com/rue-language/rue</a>
What the world needs is a more expressive language than Go, that interops with Go's compilation model and libraries.
I write a lot of go. I tried to write a lot of rust but fell into lifetime traps. I really want to leave C++ but I just can’t without something that’s also object oriented.<p>Not a dig at functional, it’s just my big codebases are logically defined as objects and systems that don’t lend itself to just being a struct or an interface.<p>Inheritance is why I’m stuck in C++ land.<p>I would love to have something like rust but that supports classes, virtual methods, etc. but I guess I’ll keep waiting.
In Rust you can have structs with any number of methods defined on them, which is functionally not that different from a class. You get interface like behavior with traitsz and you get encapsulation with private/public data and methods.<p>Does inheritance really matter that much?
Yes it does. Unless I can attach a trait to a struct without having to define all the methods of that trait for that struct. This is my issue with interfaces and go. I can totally separate out objects as interfaces but then I have to implement each implementation’s interface methods and it’s a serious chore when they’re always the same.<p>For example: <i>Playable</i> could be a trait that plays a sound when you interact with it. I would need to implement <i>func interact</i> for each object. Piano, jukebox, doorbell, etc. With inheritance, I write it once, add it to my class, and now all instances of that object have interact. Can I add instance variables to a trait?<p>This saves me time and keeps Claude out of my code. Otherwise I ask Claude to implement them all, modify them all, to try to keep them all logically the same.<p>I also don’t want to go type soup in order to abstract this into something workable.
I respect your preferences, but I am unlikely to add this sort of OOP. Ideally there'll be no subtyping at all in Rue. So you'll have to keep waiting, I'm afraid. Thanks for checking it out regardless!
As a long time C++ user, I’m curious why you like inheritance and virtual methods so much.<p>I maintain a medium sized, old-ish C++ code base. It uses classes and inheritance and virtual methods and even some multiple inheritance. I <i>despise</i> this stuff. Single Inheritance is great until you discover that you have a thing that doesn’t slot nicely into the hierarchy or when you realize that you want to decompose an interface (cough, base class) into a couple of non-hierarchically related things. Multiple inheritance is an absolute mess unless you strictly use base classes with pure virtual methods and no member variables. And forcing everything into an “is a” relationship instead of a “has a” relationship can be messy sometimes.<p>I often wish C++ had traits / or Haskell style type classes.
Any plans for adding algebraic data types (aka rust enums)?
It may have been more useful to link to the blog post [0] which gives more of an introduction than the front page at this point.<p>[0] <a href="https://rue-lang.dev/blog/hello-world/" rel="nofollow">https://rue-lang.dev/blog/hello-world/</a>
I posted that, and also <a href="https://steveklabnik.com/writing/thirteen-years-of-rust-and-the-birth-of-rue/" rel="nofollow">https://steveklabnik.com/writing/thirteen-years-of-rust-and-...</a><p>Just to link them all together. This is the one that the algorithm picked up :)
How does this differ from Hylo [0]?<p>[0] <a href="https://hylo-lang.org" rel="nofollow">https://hylo-lang.org</a>
I am very interested in Hylo! I think they're playing in similar spaces. I'd like to explore mutable value semantics for Rue.<p>One huge difference is that Hylo is using LLVM, whereas I'm implementing my own backends. Another is that Hylo seems to know what they want to do with concurrency, whereas I really do not at all right now.<p>I think Hylo takes a lot of inspiration from Swift, whereas I take more inspiration from Rust. Swift and Rust are already very similar. So maybe Hylo and Rue will end up like this: sister languages. Or maybe they'll end up differently. I'm not sure! I'm just playing around right now.
Interesting, for me the "between Rust and Go" would be a nice fit for Swift or Zig. I've always quite liked the language design of Swift, it's bad that it didn't really take off that much
One thing working on this project has already done is give me more appreciation for a lot of Zig's design.<p>Zig really aims to be great at things I don't imagine Rue being useful for, though. But there's lots of good stuff there.<p>And lots of respect to Swift as well, it and Hylo are also major inspiration for me here.
Checkout Borgo: <a href="https://github.com/borgo-lang/borgo" rel="nofollow">https://github.com/borgo-lang/borgo</a><p>I also find that D is good between language. You can do high level or low level whenever you need it.<p>You can also do some inbetween systems programming in C# if you don’t care about a VM or msft.
> You can also do some inbetween systems programming in C# if you don’t care about a VM or msft.<p>C# Native AOT gets rid of the JIT and gives you a pretty good perf+memory profile compared to the past.<p>It's mostly the stigma of .NET Framework legacy systems that put people off, but modern C# projects are a breeze.
AFAIK there’s still holes like reflection and you have some work, but if that’s changed that’s really good. I suspect it’ll be hard for C# to escape the stench of “enterprise” though.<p>I’m looking forward to seeing how it shapes out over the next few years. Especially once they release union types.
FWIW JIT is rarely an issue, and enables strong optimizations not available in AOT (it has its own, but JIT is overall much better for throughput). RyuJIT can do the same speculative optimizations OpenJDK Hotspot does except the language has fewer abstractions which are cheaper and access to low-level programming which allows it to have much different performance profile.<p>NativeAOT's primary goal is reducing memory footprint, binary size, making "run many methods once or rarely" much faster (CLI and GUI applications, serverless functions) and also shipping to targets where JIT is not allowed or undesirable. It can also be used to ship native dynamically or statically (the latter is tricky) linked libraries.
How does this compare to Swift?
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.
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?
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.
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.<p>Perhaps calling it an “optimization” is misleading. Certainly it makes code faster, but more importantly it’s syntax sugar to translate recursion into loops.
It virtue-signals that they're part of the hip functional crowd.<p>(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.)
functional programming background / SICP ?
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.
Plus we all know that fibs = 1 : 1 : zipWith (+) fibs (tail fibs) is the only serious Fibonacci implementation.
"Well you can judge the whole world on the sparkle that you think it lacks.<p>Yes, you can stare into the abyss, but it's staring right back"