I came to Rust for things like sum types, lack of unexpected null values, monadic error handling (Result<T, E>), and the borrow checker's ability to enable the typestate pattern.
(https://cliffle.com/blog/rust-typestate/ for more on what the typestate pattern is but the TL;DR: is "verifying correct traversal of a state machine at compile time". For example, making it a compile-time error to try to set an HTTP header after you've started streaming the body.)
Before Rust, I'd spent 15 years with Python as my preferred language, and I had experience with TypeScript, CoffeeScript, JavaScript, PHP, Bourne Shell, and the "used very little or very long ago, so I forgot" kind of experience with Lua, XSLT, Perl, C, C++, Visual Basic, QBasic, and DOS/Windows Batch Files.
For me, it's purely about Rust reducing the amount of time I have to spend writing Python unit tests to get the level of confidence I want in my codebases without having to put up with the quirks of a pure functional language like Haskell that doesn't put high value on long-term API stability. (And yes, I do use MyPy and type annotations heavily.)
It's also a big boost to the value proposition that, with no garbage collector of its own, it's easy to integrate Rust modules into my PyQt GUIs or Django+Celery web apps using rust-cpython or PyO3. Trying to hand off objects between multiple garbage collectors in the same address space without something like "serialize the whole thing, hand over ownership of the bag of bytes, then deserialize it" is a recipe for pain.
I also have extensive experience of every language you mention and I have coded a fair bit of Rust too. I have over 20 years of experience in Python, C, C++, ELisp, Java, JavaScript, etc. Rust forces you to spend time and effort thinking about something that is automatic in high-level languages, namely collection of garbage. It's silly to claim that forcing developers to spend more time on bookkeeping causes fewer bugs.
Testing is orthogonal to typing, and I believe that those who claim that the latter can make up for the former simply do not understand how to test software. Correct typing is the foundation for a correct program, but the lack of type errors absolutely do not indicate a lack of bugs. Logic errors that are not caused by type errors are far more common, far more dangerous, and are not caught by type checkers.
> It's also a big boost to the value proposition that, with no garbage collector of its own, it's easy to integrate Rust modules into my PyQt GUIs or Django+Celery web apps using rust-cpython or PyO3. Trying to hand off objects between multiple garbage collectors in the same address space without something like "serialize the whole thing, hand over ownership of the bag of bytes, then deserialize it" is a recipe for pain.
I've written a binding for CPython in Factor. There is some plumbing work for sure, but no, it is not that complicated. You manage objects created by the foreign memory manager using special tokens. The difficult part is callbacks; host language calling CPython which calls back to the host language. rust-cpython's documentation doesn't mention callbacks at all so I guess it doesn't support it.
> I also have extensive experience of every language you mention and I have coded a fair bit of Rust too. I have over 20 years of experience in Python, C, C++, ELisp, Java, JavaScript, etc. Rust forces you to spend time and effort thinking about something that is automatic in high-level languages, namely collection of garbage. It's silly to claim that forcing developers to spend more time on bookkeeping causes fewer bugs.
Your mileage may vary but, on average, I find the time lost to bookkeeping is dwarfed by the time saved on not having to use testing to verify invariants which are upheld by the type system.
Granted, my coding style in Python was already quite similar to what Rust lends itself well to.
...plus, I just find it more relaxing to code in a language where I can delegate more of that to the compiler's type-checker.
> rust-cpython's documentation doesn't mention callbacks at all so I guess it doesn't support it.
First, rust-cpython is the older, less advanced binding. PyO3 forked off from it to explore more advanced API designs that, at the time, required API-unstable features only available in the nightly Rust but PyO3 now runs on stable Rust.
Second, I haven't tried closures with rust-cpython yet but, for functions, the py_fn! macro is how you wrap a Rust function into something you can inject into a namespace in the Python runtime.
As for calling Python functions from Rust in rust-cpython, you call the run or eval methods on the object which indicates that you've taken the GIL.
I suppose, if closures aren't supported, you could work around it by using methods on a py_class!-wrapped Rust object instead.
Third, I avoid C++ these days and limit my modern use of C to retrocomputing projects. It's just not worth the mental effort to write C in my projects, so I stick to binding things in ways where I get a memory-safe, type-safe binding and someone else can be responsible for stressing out over the correctness of the binding generator.
> Your mileage may vary but, on average, I find the time lost to bookkeeping is dwarfed by the time saved on not having to use testing to verify invariants which are upheld by the type system.
Memory management is orthogonal to type checking. I agree with you that static type checking has advantages but that is not what I meant by bookkeeping. The bookkeeping is Rust's borrow checker, explicit memory management in languages like C, or even weak references in some languages. This is time lost which wouldn't have been lost had the programmer choosen to use a language with tracing garbage collection.
In fact, you can think of automatic garbage collection as the language upholding certain invariants about memory that the programmer otherwise would be forced to ensure themself.
Thus a Java programmer has to think less about memory handling than a Rust programmer. Less to think about means less bugs. You may argue that it is worth it because Rust is faster than Java. I have not seen benchmarks that proves that but, even so, I can count on one hand the number of times Java's gc has caused significant problems even in performance sensitive code.
> Second, I haven't tried closures with rust-cpython yet but, for functions, the py_fn! macro is how you wrap a Rust function into something you can inject into a namespace in the Python runtime.
Right, it's an engineering problem so I'm sure it's solvable in Rust. After all, it was solvable in Factor which is a gc:ed language. My point was that I doubt Rust's lack of gc makes it easier to embed CPython.
...and I'm saying that Rust is the only language where the benefit of using it for its type system has, for me and so far, outweighed the downsides of having to think about memory management.
Java? Pain in the ass with all those non-inferred type signatures and comparatively poor POSIX API integration. Also, I've yet to encounter a Java GUI, AWT, Swing, SWT, or otherwise, that wasn't buggy and sluggish under X11, and the startup time is, in my experience, even worse than the Python-based CLI utilities I'm often migrating to Rust to get improved startup time. (Also, checked exceptions don't compose well with higher-order functions. Monadic error handling does.)
C#? Not as bad as Java, but still doesn't have a value proposition that would make me switch away from Python.
C or C++? No. I use Rust for getting a stronger type system on top of something that's still memory-safe, not its performance. (Aside from the aforementioned "If Rust is offering, sure I'll take faster startup for my CLI tools AND monadic error handling AND explicit nullability".)
Vala? I forgot to mention that I played around with it and it's got all the ills you'd expect of a niche compile-to-C language.
TypeScript on Node.js? Worse than Python+MyPy in pretty much every way that matters to me except for having native sum types.
Haskell? Sorry. You'd have to pay me to code in a pure functional language, even without my dislike for its syntax and the ecosystem's philosophy of not being afraid to break APIs to advance the state of the art.
etc. etc. etc.
Still, as I've said before, I tended to already use Python for stuff that's well-suited to Rust. For example, so far, I've yet to need an Rc or Arc aside from the ones actix-web embeds in its data containers.
...and if I did, I certainly would want something along the lines of the borrow checker double-checking that I'm not introducing data races in threaded code, and a system akin to Rust's for compiler safety checks on non-memory resources managed through RAII.
...plus, it's shamefully rare to find things that match Serde for declarative serialization and deserialization, let alone exceed it.
That said, I'm a "right tool for the job" kind of guy and I still use Python for anything that involves SQL for want of a library like Django ORM or SQLAlchemy+Alembic which abstracts over the difference between SQLite and PostgreSQL DDL, doesn't use the database or a raw SQL file as the authoritative source of truth for the schema, and has a migrations system which auto-generates draft migrations by diffing the authoritative schema against the database.
(https://cliffle.com/blog/rust-typestate/ for more on what the typestate pattern is but the TL;DR: is "verifying correct traversal of a state machine at compile time". For example, making it a compile-time error to try to set an HTTP header after you've started streaming the body.)
Before Rust, I'd spent 15 years with Python as my preferred language, and I had experience with TypeScript, CoffeeScript, JavaScript, PHP, Bourne Shell, and the "used very little or very long ago, so I forgot" kind of experience with Lua, XSLT, Perl, C, C++, Visual Basic, QBasic, and DOS/Windows Batch Files.
For me, it's purely about Rust reducing the amount of time I have to spend writing Python unit tests to get the level of confidence I want in my codebases without having to put up with the quirks of a pure functional language like Haskell that doesn't put high value on long-term API stability. (And yes, I do use MyPy and type annotations heavily.)
It's also a big boost to the value proposition that, with no garbage collector of its own, it's easy to integrate Rust modules into my PyQt GUIs or Django+Celery web apps using rust-cpython or PyO3. Trying to hand off objects between multiple garbage collectors in the same address space without something like "serialize the whole thing, hand over ownership of the bag of bytes, then deserialize it" is a recipe for pain.
Also, I'd suggest reading the "What makes Rust work" part of https://boats.gitlab.io/blog/post/notes-on-a-smaller-rust/ for an explanation of why garbage collection doesn't solve all the problems the borrow checker does.