637 karma · joined September 8, 2021
But I think the distinction you made about "client-side Rust" not being as obviously-tokio-dominated as backend-side does make me stop and think.
Btw, this looks interesting regardless! Great job! Can I ask how your Rust learning journey went? I assume one needs an under-the-hood understanding of async Rust to build a library like this, and I'd love to hear about your learning journey getting to that understanding.
What's the case for handling code and data separately? In my experience, the primary motivation for using such a tool are easy reproducibility through easy tracking of code, hyperparams, and data. It's not obvious to me how that goal would be advanced by tracking code and data separately.
Unless you present your engineering qualifications that are of equivalent credibility as that of the aforementioned people, as well as present evidence that you've worked with him as closely with Elon Musk as some of these folks have, I'll stick with my conclusion that, yes, he's indubitably an engineer.
[0]: https://youtu.be/PlLmdTcsAWU?t=1439
[1]: https://twitter.com/ID_AA_Carmack/status/1038832124747571200...
[2]: https://www.youtube.com/watch?v=GNG6ZzDh9C8&t=390s, https://www.usatoday.com/story/news/nation/2020/05/26/spacex...
[3] https://twitter.com/lexfridman/status/1225875212559933442?la...
[0] Much more CPU and RAM needed.
[1] You rarely work with a fresh OS install; you'll have to install things, edit files in /etc/ or whatever, set firewall rules perhaps. For me at least, many of this comes with googling, say, "how do I allow an incoming TCP connection for a port range?". Ie, tiny details I don't want to memorize. I'll have to replicate these steps between the dev VM and the prod environment. If I use Docker, I can write these things into the Dockerfile and then just do "docker-compose up" in production, and it will just work. Also, I can commit that Dockerfile into a repo. It makes it much easier for others, or future-me, to understand what the dev-ops setup is like.
To my knowledge, no. If you want to push to a vector from multiple threads, you "wrap" the vector in a `Mutex`. The difference between C++'s std::mutex or std::lock_guard and Rust's Mutex, is that the compiler refuses to let you touch the data protected by the mutex unless you have a lock acquired on the mutex.
> Is there any provision ...
Yes!
1. In Rust, types can implement "traits".
2. There's two traits that control thread safety: `Send`, and `Sync` Basically, any type that "implements the trait" `Send` is safe to send across threads. And a reference of any type that implements `Sync` is safe to to send across threads.
3. `Send` and `Sync` are "auto-traits", which means that if you make a data structure out of primitives that all implement `Send`, your data structure will also implement `Send`, same for `Sync`.
4. There are a bunch of thread-safety primitives that you can use (like `Arc` (atomic reference counters), `Mutex`, ..etc) to build thread safe data structures.
> how does that work in terms of compile-time errors?
The compiler will not let send a type across threads if it doesn't implement `Send`, and it won't let you send a reference to a type if the type doesn't implement `Sync`!
That way, if you avoid using the `unsafe` keyword, and the compiler agrees to compile your code, you can be sure you won't have data races!
(1) Go through the Rust Book,
(2) Do "exercises" (small side projects, advent-of-code, rustlings. ..etc),
(3) Go through "Rust for Rustaceans". It's specifically targetted at "intermediate Rust developers". If you have "good working knowledge" of C and C++, I will wager that you will absolutely love this book, because, to me, (1) it explained so much of Rust's design choices, and (2) you will learn so much so quickly.
Avoid the Object and {} types, as they mean 'any non-nullish value'.
This is a point of confusion for many developers, who think it means 'any object type'.
https://github.com/typescript-eslint/typescript-eslint/blob/... trait LendingIterator<'a, T> {
fn next(&'a mut self) -> Option<&'a T>;
}
impl<'a, T> LendingIterator<'a, T> for Whatever {
fn next(&'a mut self) -> Option<&'a T> {
todo!()
}
}
And of course, you'd modify the for loop syntax to desugar appropriately if the the iterator implements LendingIterator.If so, thanks!
Through I don't yet "see it" that C++ has safety primitives that "equal" that of Rust's, I've learnt a lot about C++'s safety features from other folks' replies in this thread. So again, thanks!
Thanks for the replies about the other stuff!
E.g., if you're implementing an iterator that takes a reference to the length of the container being iterated, and you require that that reference is constexpr, that means the container will have a fixed, compile-time-known, size.
Since you didn't reply to the other parts of the comment, I'll presume that C++ doesn't have the mutex I'm talking about (apart from this attempt[0], which I just learnt about and commented on in a different reply), and that it's not possible to have non-ref-counted shared references while statically avoiding dangling pointers.
An extension to C++ that adds (pretty much) a type system on top of C++ to enable static analysis and prevention of race conditions. Widely used at Google.
Super interesting! That's exactly what Rust is doing too, except I think Rust's version (which pretty much relies on two "traits", `Send` and `Sync`) is much simpler, much more ergonomic, and much more complete.> You can prevent dangling pointers using strong/weak pointers
I presume you're talking about unique_ptr/shared_ptr/weak_ptr right? If one avoids raw pointers and uses those, I think you cannot have a shared reference without ref counting right? (because shared_ptr uses ref-counting). Not that this invalidates your point, just thought it was useful to point out.
> how would a rust program handle a handle to the currently playing audio device being unplugged and invalidating your handle?
I've never had to deal with that situation, but I presume some mechanism similar to how the mutex in the stdlib has a .lock() method that returns an Option<MutexGuard<_>> (which forces the user to acknowledge/handle the case of a poisoned mutex).
> Constexpr primatives/functions have compile time guarantees and can't be changed.
Consider the c++ code here:
int a = 4;
const int &b = a;
a += 1;
Such a thing would be disallowed in Rust, which (though hard to see from this example) is the exact mechanism that prevents a Vec being modified while it is being iterated on. Does C++ have primitives that provide such guarantees?