5 ms·
I keep trying to get into rust but I always hit a brick wall when looking at examples and the code looks so complicated. Examples like this straight from the ru
by seanalltogether 2y ago
I keep trying to get into rust but I always hit a brick wall when looking at examples and the code looks so complicated. Examples like this straight from the rust website just make my eyes glaze over
struct Task {
future: Mutex<Option<BoxFuture<'static, ()>>>,
task_sender: SyncSender<Arc<Task>>,
}
- swiftcoder 2y agoI'll grant you that is probably too complicated an example to appear early in the docs, but how often are you actually building multithreaded job dispatch systems from scratch (which appears to be what this example is doing), and how simple would it be in other languages?
- diggan 2y agoThis is how simple it would be in Clojure, for comparison: (defrecord Task [future task-sender]) Probably used something like this: (defn create-task [] (let [future (atom nil) task-sender (async/chan)] (->Task future task-sender))) Not sure it makes sense but a pretty much direct translation as far as it goes.
- galangalalgol 2y agoHaving never used a lisp outside an AI class a quarter century ago, that isn't comprehensible to me, and I don't think it is just my lack of caffeine. The rust example makes more sense, but I've used rust for almost a decade at this point and c++ for over three. Familiarity matters.
- diggan 2y agoYeah, of course. If you show me Brainfuck or APL code I won't be able to make head or tails of it either. But familiarity should never stop one from learning new things, otherwise we miss out on a lot of simple, interesting and useful stuff :)
- Sharlin 2y agoBut honestly only for a very short time. It took me two weeks of learning for Clojure to start looking completely natural to me. Surface syntax is such a trivially minor thing, which is why it seems ridiculous to me that the OP author even mentions things like "Swift uses switch rather than match, such familiarity" among other much more solid points.
- speed_spread 2y agoSo it's the same but without type annotations? Meaning that the clojure IDE has a much harder time providing completion and that you need to write all sorts of tests to get the same guarantees as the rust compiler provides for free. Type systems aren't just about memory representation, they also tell the next programmer about the intent and performance characteristics of the code at hand.
- diggan 2y agoIf you really badly want types, you'd slap something like this below it: (s/def ::future atom?)) (s/def ::task-sender async/chan?) (s/def ::task (s/keys :req-un [::future ::task-sender])) (s/fdef create-task :ret ::task) (stest/instrument `create-task) But then I don't think you'd reach for something like Clojure if static typing is something that you require.
- tmtvl 2y agoThat's why I moved from Scheme to Common Lisp, it's nice being able to do... (defstruct Task (future :type Future) (task-sender :type Sender)) And have SBCL warn me when I try to jam the wrong type in.
- nurettin 2y agoIt would be ridiculously simple in go with channels.
- kenmacd 2y agoWould it though? Just running things 'async' is simple, but it's also simple in Rust. I wouldn't figure writing an Executor would be any easier in Go than the Rust example at https://rust-lang.github.io/async-book/02_execution/04_executor.html https://rust-lang.github.io/async-book/02_execution/04_execu...
- nurettin 2y agoAs I understand, the idea an executor is to emulate an event queue using threadsafe (hopefully lock-free) queues. Since the language already has goroutines, channels and group waits, I don't think this is something that needs to be built in Go. I mean you can do your rate limiting and retry handling logic outside the language primitives. If you do have to make a task spawning executor, you can do that with channels as well. But it would just be a very thin wrapper.
- sorentwo 2y agoTransparently parallel, lightweight tasks are part of the language in Elixir. Fully multithreaded and able to utilize all cores out of the box.
- foldr 2y agoYes, but the Rust code is the kind of thing you'd write if you wanted to build that kind of thing yourself. I do think normal Rust can get too typey, but this is probably not the best example of that.
- jppittma 2y agoIn golang I think just type Task struct { Sender chan Task } no?
- draw_down 2y ago[dead]
- XorNot 2y agoHonestly that one I can read (I am just now in earnest trying to learn Rust). What I've been banging my head into is trying to get a suite of boilerplate libraries together to make basic things happen. Got directed from slog to tokio-tracing for logging, so dove into setting up my basic "--log-level", "--log-format" CLI options I add to every non-trivial program in Rust and ran into...whatever is going on with configuring it (namely, tokio-subscriber seems to encode all the configuration information into types, so you can't just return a Subscriber or Subscriber builder from a function and then add it as a logger...the documentation gives no hint on what trait or type I'm meant to return here).
- deleted 2y ago[deleted]
- guitarbill 2y agoNot just you. Best case the tracing ecosystem is weird and the documentation is lacking, but all of the weirdness makes it very performant. Worst case it is weird and badly documented and over-engineered. Since rustc uses tracing I really hope it's the first...
- junon 2y agoYeah this is a bad example for introductions for rust - namely that async rust is something I'd consider advanced. However the type explicitness is, IMO, one of its strengths. It lets you build up types that e.g. in C++ we're not a given, had properties about the behavior buried in docs, etc.
- pjmlp 2y agoIt would be, if there wasn't a trend to expose async all over the place on the Rust ecosystem. Thus even newbies get to bump into it quite fast.
- kenmacd 2y agoThey run in to async, but do they run in to the internals of how that code is executed? The chapter that example is from includes the disclaimer: > In this section, we'll cover the underlying structure of how Futures and asynchronous tasks are scheduled. If you're only interested in learning how to write higher-level code that uses existing Future types and aren't interested in the details of how Future types work, you can skip ahead to the async/await chapter.
- pjmlp 2y agoNaturally, as soon as they get some kind of async related compilation error.
- junon 2y agoDepends on the use case. Rust has more explicit types, yes. It's kind of a weak argument against the language though, in my opinion.
- bitexploder 2y agoThe only tricky thing there is the BoxFuture and ‘static lifetime. Arc is pretty simple. Option is simple. Rust is just forcing you to acknowledge certain things and understand lifetimes explicitly before you can move data around. And static is close to a “plz make this lifetime error go away” thing cause it means it can live forever. But they are also fine, and often the right choice. This may seem contradictory but it will make sense if the Rust compiler hurts you enough times :)
- smodo 2y agoThis may demonstrate the intricacy of the type system but isn’t a very intuitive example… I feel like the Rust Book does a good job of explaining the different types in std. For one thing it shows these types in their most idiomatic use cases. Rust is my first real low level language and I learned a lot by reading the book. And by reading it I mean I actually read the thing front to back before touching a keyboard. Then I started to experiment with some simple code and after a couple of months I had some actually programs that I use often. Obviously I rewrote them after a year but hey ho, it’s all a learning experience.
- hu3 2y agoIt's not just you, I watched yesterday [1] that Bun's author initially tried to use Rust but wasn't as productive. Then he switched to Zig and here they are, innovating JS/TS landscape. [1] https://www.youtube.com/watch?v=eF48Ar-JjT8&t=670s https://www.youtube.com/watch?v=eF48Ar-JjT8&t=670s
- MSFT_Edging 2y agoSomething I like about Rust is that it gets very specific and explicit. On first glance you're like wtf is going on, but you can derive backwards what is happening without having to look for a discussion on language behavior under the hood. Automagic is nice sometimes but I'm the kind of person that needs to say the each word of an acronym to process it. Rust is like that in a way. You have a mutex on an option type, the option has a heap allocated future that contains data that can live for the lifetime of the program. This is clear to me because I don't need to fill in blanks. My memory is terrible, I've forgotten so many things I've learned in the past. I could pick them back up quite quickly but I don't have the little facts ready to go. If i wanted to use that future, I know I need to check the mutex lock, check if the option contains a Some(), etc. Sure this isn't for everyone, but I'm glad we have a tool like this gaining popularity. I have little interest in studying the arcane knowledge of C++ and sorting out what is current and what is obsolete, then arguing with a 30 year veteran that their technique is 20 years stale.
- j-pb 2y agoIt looks complicated but if you think about it as a series of property descriptions it becomes much easier. I'm gonna make it worse first by substituting the `BoxFuture` type alias with its definition but that makes it easier to explain. future: Mutex<Option<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>>, What this means is that the future field stores something that is a: - Dynamically dispatched object (with a V-Table) that implements methods from the Future trait, and returns nothing () = void. (dyn Future<Output = ()>) - That thing needs to be sendable between Threads (+ Send) - And must potentially live forever (+ 'static) - The dynamic dispatch stuff only works with heap allocations because the V-Table pointer is stored in the double wide smart-pointer. In this case we use a Box which means only one thing/thread can hold it at a time (unlike reference counted Rc/Arc). - That allocation of the Heap allocation must not be moved around in memory. (Pin<...>) - that thing might not be there (it can be Null), but replacing/taking/putting the thing will be secured by the mutex so that's a nice guarantee for multithreading (Option<...>) - access to a thing is synchronised via a mutex (Mutex<...>) So while it looks horrible at first sight, it is simply telling you a lot about the guarantees of that particular type.