3 ms·
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
by ssokolow 5y ago
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/ 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/ 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.
- bjourne 5y agoI 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.
- ssokolow 5y ago> 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.
- bjourne 5y ago> 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.