4:13 shouldn't that be "REPLACED" instead of "REPLACE"? not nagging, I love your videos and found an excuse to leave work early when I saw the notification for this one!
Damage is a transition that changes Mario’s state from Fire to Super, from Super to Mario, from… hold on, I need to compile this to make sure I got them all. :-)
I’m going through “Writing an Interpreter in Go” (Thorsten, 2018) but instead of Go I’m writing the program in Rust, translating the code as it’s presented. The Rust version using enums is *so much cleaner* then the class based system presented, and I get to skip whole sections when I realize that he’s implementing something that I already have for free. I’d highly recommend the exercise.
Oh yes, I have done something similar with the "Crafting interpreters", which code was written in Java. functional style with variants to represent AST nodes and pattern matching made it so fun
This is my favourite part of rust! As someone who works within robotics, representing state and transitions is vital, both for safety and for ensuring confidence that the system will be in well defined states for the lifetime of the program. With so many other languages, a small change in the represented state would correspond to an unknown amount of work to correct the system behaviour elsewhere, as you just don't know that it's wrong until you reach that part of the code.
@@cotton_flowers I have an itch to port WPILib to Rust but I find it nontrivial to essentially rearchitect the whole thing to not use inheritance and such
here's your excuse: to become a better programmer (not just coding and crossing fingers) while also coding in language that put zealous concepts to extreme and was built around them
Your long search for a good state machine example was certainly worth it! Not sure how you just casually looked at the super mario world manual though haha, but I'm glad you did! Thank you as always for your great content!
Ha! yes well, though I have MANY retro computers and consoles, I admit that manual was PDF, discovered by the youtuber linked on the slide - though he didn't go into it! I thought I would :-D
Having learned Haskell just before Rust, enums felt just right. And since now having spent a lot more time in Rust, I always feel good when I use a nice match expression! Thanks Tris!
When I was first starting to learn Rust coming from Python I was struggling to see the benefit of enums, but then I realized you HAVE to use enums to represent certain data structures. At first that felt restrictive, but then I realized that restriction relieved so much mental burden I'd have otherwise spent in Python thinking about the structure of the program just so that I, the one writing the program, would be able to understand it. In Rust more than any other language I feel like I can just DO things, rush them even, and the compiler is like R2-D2 was to Luke Skywalker, guiding me and filling the gaps I'm unable to fully grasp in my limited human working memory. When my code does start to get unwieldy, refactoring isn't some mess where everything becomes more confusing, often as a program grows it seems little slots arise where you know a certain type (like an enum) clicks in like a puzzle piece. My two cents of advice to people who want to learn Rust coming from more OO languages: 1. ```struct != class```: Be careful with Impl block methods and the "self" keyword. Structs are purely data and methods are syntactic sugar for functions, as they always are, but this relationship is much closer to the surface in Rust than it is in other OO languages and you'll hit some frustrating roadblocks if you accept "struct == class" at face value. 2. Avoid Traits except where the compiler tells you you need them or an API requires them. (i.e. #[derive(Clone, Copy, Debug)] ). As you learn you will find places where they fill a niche, but shoehorning them in as a replacement for c++ templates/js interfaces/python protocols is a recipe for unreadable spaghetti code. 3. USE ENUMS. ALWAYS, ALWAYS, ALWAYS. They are fucking awesome, as this video handily outlines.
In my experience enums are not the medicine for every problem, i used to overcomplicate some programs since i was so amazed by enums too, that i felt i need to use it everywhere. But later i realised what a mess i have created
Hey since you describe yourself as a guy who transitioned from python to rust ? How was it ? Was it that difficult ? I am a pro in python. Will I be able to learn rust faster ?
One of my favorite UA-cam channels even though I don’t really watch the videos. It’s just something about the overall style and personality that bleeds through them. There’s no filler and there’s no commercialism. It just feels like a sincerely enthusiastic person trying to tell you something clearly that actually is of interest. It definitely hugely has led me to be sure to check out more Rust whenever the opportunity is there. I still hear his swinging British voice in my head when I try it, like, “In Rust, all you have to do is read the compiler message carefully.”
Thank you so much! Did you know I also make an audiofiction podcast about AI, tech, and mental health? I'd love to know what you think! ua-cam.com/video/p3bDE9kszMc/v-deo.html Season 12 just dropped this week!
I love enums, they're a really nice way to represent "choices" in the type system, rather than writing ugly if statements that try to verify everything with a bunch of bools
That Mario example is what I've needed for months! That's so good. And tying it to the actual code, chefs kiss. I finally get state machines, and I have a tangible idea of how it can be made. Thank you!
I pretty consistently worry that I am watching your videos too soon, having barely started with Rust, and the elegance of your explanations prevents brain fog.
its not you, its too dense and too fast. My 2 cents
Рік тому+14
One of my favourite applications for state machines is fighting game input parsing. In a game like Street Fighter, a particular special move might require a sequence such as down, down-right, right, punch. But the games build in a bit of leniency, so your timing doesn't have to be exact. A great way to read the player's inputs then is to create a state machine for the special move, feeding the last several frames' worth of inputs through it. It makes it easy to define leniency as well as failure states in precise terms.
As someone who is currently learning rust, i just finished the chapter on enums from “the book”, and i was able to understand and comprehend whats going on, hats off to you for explaining a what seems like complicated topic in such an elegant way !! This was both a useful rust video and an explanation of how machine state logic works! Amazing
I haven’t started playing with Rust yet but if the amount of stuff I’m consuming about it is an indication it’s going to happen any day now hah! I will say though that “Enum”s are a bit scary for me, I’ve been burned too many times. I’m not sure I can think of a single time I’ve used an enum in a language, metalanguage, db, framework, engine, what-have-you and *not* lived to regret it. I probably wouldn’t have even thought twice about it had they gone with another name :P
I don't know Rust but it's really fun pausing your videos on code snippets and compiler errors, trying to figure out what it means and being successful almost every time!
Can agree, after getting comfortable with rust's enums I tried going back to typescript, but they had limited enum support and it just didn't feel the same as Rust.
FYI, you can use sum types in typescript as well. It's not as clean as in rust, but you can represent the cat example pretty well there. They are just not called enums. What typescript doesn't have tho is an equivalent to the match expression that makes the enum feature so powerful in rust.
Interesting to see a nice algebraic type system and exhaustive pattern matching outside of a functional language. This was always my favorite feature of F#.
Rust is actually a hybrid functional/imperative language, it's not a pure functional language. I think this is part of it's genius, and I see this everywhere in the language: Sensible defaults, but with escape hatches for practicality. (like the unsafe system)
Great as always! I'm currently writing my Bachelor's Thesis and I have utilised Rust for a lot of the implementation, and its growing more and more on me, thanks for introducing me to this wonderful language :)
On the topic of making invalid states unrepresentable and normalized representations of data, I recommend the excellent essay by Alexis King by the title "Parse, don't validate", it's in haskell but I believe its still relevant enough. Great vid btw :)
I really enjoy the idea of modeling states and find it far more intuitive as a developer than OOP (especially the make many classes just to inherit down to the one I actually use). I am sure we can get around this by simplifying but my mind immediately tries to explore the edge cases where it's difficult to model the states directly with a finite state machine - such as programs that might simulate complex biological systems. It certainly doesn't abrogate the utility of representation by states but it does bring into view some interesting ideas of more fluid states of giant matrices of floating point numbers.
Amazing video as always. I have kind of thought of this: I have recently had an OOP design patterns course and a functional programming course and I thought:"there must be an easier way of implementing these patterns functionally". Turns out you can indeed
C++ has been learning a lot from Rust, and this kind of enum has been implemented in the Standard Template Library as std::variant. The match described here would be analogous to std::visit executes a function on the current state of the variant, and requires that the function being executed has a valid overload for any of the types declared.
C++ learns from *every* language, and that's kinda the problem, right? :-D Someone commented on one of my videos something I think of often "If you almost never add features, you get C, if you almost never remove them, you get C++"
Great video as always! Recently had to do a project that would need to be super reliable as it would fetch data constantly, and match arms to make error handling have just been great. Together with loop flags the code just looks clean is super readable
As soon as I tried out enums and match in rust. I completely fell in love with the language once again. And it's intuitive, everytime I use emus and match statements, it FEELS very familiar, yet, not.
This is very informative. I've always struggled with data modelling, in rust, in particular. Will be saving this for many rewatchs. As always, thanks for the quality content.
That was great, gave me some inspiration for a state modeling section of an embedded program I'm working on. And I really appreciate the no-fluff, information-dense, focused approach on this video!
In the 1980s and 1990s, there were many programming language designers working on OOP (also functional programming), such as Luca Cardelli, who was involved in the design and development of Modula-3 and ML, Philip Wadler also worked on Java and Haskell. We can see that the history of OOP and FP is closely related. However, due to the excellent marketing techniques of Sun and Oracle, some people later believe that Java has the sole right to explain OOP :P
My pleasure! I hope everyone gives Rust a go, have you seen my whole Rust series? I'd start with ua-cam.com/video/2hXNd6x9sZs/v-deo.html&lc=Ugy1Nxse098tA5xqmTB4AaABAg
Really cracking video. I'm just starting out with Rust, approaching as a full reboot of my understanding of programming. I found the video very helpful. Great example with Mario..
Hi, You have given me so many cool insights and delimited them so nicely from concepts that I knew from other languages allowing me to see what the purpose they have and what the reasoning behind them are. You have in that way contributed to how I go about my work and in turn. Thanks so much.
The specificity in this video was excellent, I feel like I both got an idea of the theory and a concrete relatable implementation that can scale to any size. I loved this format to present the content. Well done!
I love enums. They're the rust feature I find the most useful for actually representing the problems I need. In most other languages they are less strict, less useful or dont exist at all.
Nice, looking forward to learning rust - my goal for the next weeks. I'm an experienced scala developer and was happy to see lots of familiar concepts. Let's see how far I get. Thanks for your great, concise and motivational videos!
Thank you so much for these videos! My first language was java and I think it really messed with me for years so I never felt like I could actually make anything. OO is all I knew, so it's all that made sense. But the last week or so learning rust, programming finally feels... natural to me instead of a frustrating byzantine experience. Though I did play with C, all the memory and boilerplate I had to make and use really scared me off, making me feel stupid.. Rust makes me feel like a genius, like you said!
Thank you! Did you know that the latest Lost Terminal 10-episode special is set on the North Island? Just wrapping it up, I'm so proud of it, it's called "41: South". It's a Patreon special, but the first two are public!-> ua-cam.com/video/EElC_1LbyOI/v-deo.html I'd love to know your thoughts. (The main public podcast that I write produce and perform is here if you're interested, also it provides context to the special: ua-cam.com/video/p3bDE9kszMc/v-deo.html)
You can do something similar with phantom data types, like having a type called `Container` that can be locked or unlocked. This is useful because you can enforce state at the API level, for example you, if do `impl Container { fn open {...} }`, the open function will only be usable when the container is in the unlocked state.
@@NoBoilerplate I apologise, wasn't trying to imply that the method you were showing wasn't clever or anything, I just wanted to share something I thought was cool.
Great video! My one piece of advice is: this works in Rust because of its language-level features. If you’re working in an OO language (like I am for my day job) you may end up with something that is both non-idiomatic and also doesn’t provide the same guarantees as the Rust equivalent. I have been guilty of this and it does not turn out like you’ll want it to 😅 All the more reason to oxidize your codebase, though!
@@NoBoilerplate thanks! 🫡 I’ll say that the most recent Java iterations have introduced “sealed interfaces” and “record classes” which, combined with some improvements to pattern matching in switch statements, actually make some of the techniques you outlined more feasible (although still inferior because it’s much more verbose and the guarantees aren’t quite as strong). It’s quite illuminating of just how badly people want these features, though
I am a old school DBA and data modeling scientist. Your video has convinced me to look further into the Rust language. Old dogs can learn new tricks. 😆
The fat enums with serialise macros also make it impossible to have invalid messages in client/server communications, in particular webserver/wasm, both written in rust. No need for Ajax or other url scheme, just specify operation as enums with parameters. Compiler prevents invalid message generation, and all messages must be handled
Sum types can be simulated in languages that don't have them using interfaces/abstract base classes, and distinct types for each variant. For example instead of a single Cat class/struct, you'd have two classes: AliveCat and DeadCat. That way you can have just the alive cat implement the IAliveCat inferface (which can get hungry) and the implementation is of course up to you. Of course both of them ca implement a base ICat (marker) interface. At the end of the day, the way to correctly model a domain in your programming language of choice is mostly a matter of skills.
I have a question: in the Mario example, the "match" is used against the tuple (a, b) so the compiler could cover all combinations of two enums. I understand that the state machine will never reach an undefined state, but as long as the "_" (the rest of the conditions) is used in the tuple, compile can't tell whether a wrong default condition logic is given when we add new options in to the enums, am I right?
What would be best practices of doing | representing layered state machines? Like how an app needs to handle network requests & responses, user input, and results from long running tasks?
Any language that has enums is a good language. I'm really confused that go didn't add them from the start, big miss there! Rust's also go like 10 other wierd features that make me want to code in no other language, here's a playlist if you're interested ua-cam.com/video/oY0XwMOSzq4/v-deo.html
2:41 i have a question what would happen if multiple branches of the match statement were true like for instance, what if you add 13 to the list of prime numbers then will both primes and teen will both evaluate to true?
Match statements are executed in order, the first one that matches will be executed. For the example you proposed, that would mean that only the prime branch gets executed.
This is actually the case in my example - (mario, mushroom) is more specific, and (_, mushroom) is less specific, but because it is later it is covered by the earlier case. This is what "short circuits" means - first to match matches
As a fan of Rust I have to acknowledge that Typescript's algebraic types are even more expressive. You can create union or intersection types by enumerating existing types or existing values. E.g. type result = number | boolean | "error" | (mytype & myothertype). And it also does OOP the right way, by supporting structural, as opposed to nominal subtyping (just like Rust).
One issue with these union types to represent errors is that sometimes you want the success and failure types to actually be the same type, but distinct from each other. A simple example is binary_search which returns Result. Ok(usize) means that it found it, this is the index, and Err(usize) means it didn't find it, but this is the index where it needs to be inserted.
@@TinBryn Sure, they CAN be of the same type but they don't HAVE to be. Having said this, TS doesn't support pattern matching so union type flexibility remains as a hugely underused superpower. Hope Microsoft will learn the lessons of Rust.
This is such a powerful type system that no one really knows how to do a good job of static type inference/check for intersection/union types (including basic structural subtyping), even though subtyping have a long history. The first whole-program type inference algorithm for subtyping didn't appear until 2017, and further extensions with intersection/union types won't be released until 2022, there's still a lot of work to be done (look for MLstruct or MLscript if interested, which is the state of the art). So the current languages that use these features either dynamic type checking or introduce gradual typing like Typescript.
I would make NormalPost has many PostImage and when it comes to data/information modeling. Not sure if normal forms applies really in this case, modeling a domain doesn't need to follow DB design.
Hey! This is again a really great video. I get that your channel is really about showing what Rust is capable to do but I am really interested in understanding the state of rust for embedded systems. As it's stands it is generally a domain lacking great resources to learn it and I think it could greatly benefit from Rust structured way to go about things and its great community focused on creating resources for other to do great things. Would you consider making a video about that topic or could you recommend one? It's always a pleasure to see that you have uploaded a new video (or podcast)!
while i understand your issue, would it make sense for someone who hasnt done embedded Rust to make a video about it? wouldnt you need at least a few months of experience to be able to accurately represent the state of it?
Thank you so much! I am afraid to say that though I'm really interested in embedded development with Rust (I talk about it enough don't I?) the tooling isn't quite there yet. When it is, and I can build some non-trivial projects using arduino etc, you can be sure I'll do a video on it!
@@NoBoilerplate Glad to hear that! I hope the best for this area of programming. It has so much potential for home automations or other cool projects bringing things to the real world.
The only thing I'd change in the example is to make collect return a Player instead of mutating the self.state field. Of course, this depends on how the wider system behaves, but one should always default to immutability unless you have a strong reason not to.
Yeah, in an early draft I used the chain pattern to do exactly that, however I decided to go for this more familiar way, so as not to hit folks with too many new patterns all at once :-)
That is not a good idea. To see why, consider what happens if you store Player as a field in a struct. You can't just take it out, shove it into a function and then pluck the hole with the return value. You would have to take the overarching struct apart and put it back together to do that. Taking shared reference is preferred here, as it lets you modify Player in place.
The Rust API guidelines discuss that in the Type Safety section, Non-consuming builders and Consuming builders. They say taking &mut self (i.e., non-consuming) is preferable when you can, but if one of the methods needs to take ownership, then all methods should take ownership so you can still chain them. The standard library has examples of both cases, e.g., std::process::Command for non-consuming and std::thread::Builder for consuming.
You can make error state unrepresentable in every good language (I often do it in Java), the reason we have so many IFs is because we are writing client facing applications that have to handle bad input, you can't make 400 Bad Request unrepresentable.
my favourite web frameworks in Rust, Poem-openapi, accepts client input in terms of data types, validated automatically by the compiler, not manually with ifs. It's wonderful! (docs.rs/poem-openapi/2.0.26/poem_openapi/) Your data model (using enums and structs, just like in this video) becomes the compiler-checked contract between your client and server.
Algebraic data types are great for expressing data models and are understandable even by the business people. Together with Typeclases one can get closer to expressing the “real” world concepts.
It's interesting that you bring up the ORM vs. DAL divide -- I have personally not once ever heard of the term DAL before, and even after googling it a little, I still fail to see how it is really an opposite to an ORM in any way.
Great video! I didn't necessarily think of rust enums as being so helpful at preventing invalid states (btw, at 8:37, I think std::u8::MAX is deprecated :))
This video was *awesome*, thank you! I thought the quip about tables existing long before and after OO was maybe too witty to be true and a quick google had me wanting to a “well actually…” comment. Those suck and a little more googling showed that the history of these things is murky and entirely dependent on how you chose to draw the lines. I also don’t think leaving that kind of comment even works since it’d comparing very different “types” of things; the coining of “third normal form” v. the coining of “object oriented programming”: one hasn’t changed since 1971 (3NF) and the other continues to “evolve” since the initial coining around the same time (maybe “mutate” is a better descriptor!). No matter what, your point works very well :)
Rust's Enums make me dread working in any other language - they're just so powerful and I wish that the equivalents in Python and TypeScript were as powerful. It's technically possible to do it in both, but Rust just makes it so nice to work with.
This is one of the videos I'm gonna save for future reference, or as a quick link for friends who ask me "what's so great about rust?" Really well explained! I have a minor question about the very first point in the video about sum versus product types. Can't sum types be represented in any OOP language using either interfaces (and implementing classes) or subclasses with different attributes? Obviously you don't get things like match statements, which are absolutely lovely, but is there anything else I'm missing about why this is something that OOP languages can't do? Thanks!
OOP languages can do sum types. If your OOP language supports downcasting and enumerators, than you can implement sum types quite easily. The issue is the boilerplate code you need to make it work. It's usually not ergonomic nor foolproof to implement sum types in language that lacks first-class support for them.
Exactly, and because they're not first-class citizens, the compiler is not optimised to rationalise about them. It's like saying you can build strings in any language that supports integers! :-D
I know this isn't the topic of the video but it was only a week or two ago that I was saying that I didn't understand why nonone had made a spreadsheet app that supported python in the cells. Seems so obviously brilliant
4:36 Wow, it improves all things I hate about ms excel. Basically excel was created for non-programmers and that's why it's extremely unintuitive for me to do nontrivial things.
It's not exactly the same, and it's not completely there yet, but Java is working on bringing this style of programming to Java with the features: sealed classes, switch expressions, and pattern matching in switch. They call it "data oriented programming" and it's one of the over-arching goals of Project Amber - minor improvements to the language where the sum is greater than the parts.
Do you think Rust can implement something like Polars for NdArrays? I personally use Polars and Xarray for Climate Data Science and we really need the performance bump that Polars gives us for GeoSpatial Data?
How does this compare to Kotlin’s sealed classes/interfaces with objects and data classes, and pattern matching with `when`? As far as features go, they seem to be equivalent. Is the only difference performance?
The key thing for me is that enums (ie sum types) existed in Rust from the start. The whole language is built upon enums (such as the Result enum) whereas it's just one of many options available to you in Kotlin. Building your language on Sum Types is MAGICAL!
What would be the challenges faced if we were to do the Mario example in Java using the state pattern? (see "gameprogrammingpatterns" by Robert Nystrom). Apart from the lack of pattern-matching, it seems like it would be very similar. But I guess the Mario example doesn't use tagged unions, and if we had to, the java code would be more complex
From a pure technical point of view, Java's classes are not zero-cost abstractions, they incur runtime penalties to use. Rust's impl blocks are just functions, no overhead! From a design point of view, I can only suggest that thought you CAN do almost anything you like using classes (see the GoF book) if you have first-class functions and sum types, you don't need to.
I have been looking to model some more complex things. For example, finite groups. So far, embedding the group into some numeric type has worked best for me. This isn't very pleasant because there are usually many possible numbers that do not represent elements of the group. I want to use a system like this, but there is no way I can just write out all of the operations by hand when working with something like the 10th alternating group (10!/2 elements). Is there an intelligent way to do these kinds of things in Rust?
Yes, this is what a macro would be used for - programmatic building of enums - you can either make your own (I'd use macro_rules) or see if someone has made this for you, by searching on crates.io
So I wonder, if in rust you put structs in your enum, what will be the size of an enum value? Does it work like C union but safe or is it gonna get big?
I'm not much of a low-level programmer, Rust is my first such language (though I'm having a great time!) but The Book has the answers you seek doc.rust-lang.org/reference/type-layout.html
@@NoBoilerplate Thanks. It seems like it does work like C unions after all Tbh I'm not low level coder myself, it's just good to know information. Knowing this I could add to your example from video another one about implementing blackboards. Common technique in game AI, basically a key->value dictionary where value can be multiple different types (e.g. Vector, int, pointer) so you don't want all this stuff to add up and make struct too big. You also don't want virtual functions here, obviously. Rust actually would help a lot here by ensuring that I would never read value of a wrong type while keeping struct size reasonable Wish I could actually use it, but Unity is C# only :(
Yes, Rust enums are by default represented as a pair of discriminant (a classical integer enumerator) and a C-like union of structs. Sometimes the compiler can do layout optimizations, like hiding the discriminant in padding bytes, or using invalid values of one variant to represent another. As a rule of thumb, Rust enum will have the size of its biggest variant + one or two bytes for the discriminant.
I have question, I'm designing an operation that outputs Option. When HashSet has zero elements, it should be None instead. Is it possible to have compiler check for the code if it can create empty Set? In this case, Some variant with empty set would be invalid state.
This would be a more advanced usage of Dependent Types (en.wikipedia.org/wiki/Dependent_type), a feature Rust does not have. Generally, the feature is that you can make the Type depend on the value. but this leads to a lot of complexity in the type system which may make it harder to be productive or understand code.
You declare a new type that has Option as a private field. Then you build an interface around it that guarantees the inner field is either None or Some("nonempty" HashSet). The constructor could look something like this. struct MyType( Option ); impl MyType { fn new(v: HashSet) -> Self { if v.is_empty() { Self(None) } else { Self(Some(v)) } } } Accessing the inner hashset mutably would be an unsafe operation. A safe alternative would be a method that gives a "borrow guard" type, that derefs into &mut HashSet, and fixes the parent MyType in its destructor.
Laund and Kohu have answered very smartly. If it were me, I'd redesign my whole application around making my data representable around enums. Not always possible!
I’m building my first desktop app for my FIL’s work. For simplicity, I’m writing it in Python using a custom TUI package. The core interactivity of the app involves mapping key presses to actions, which depend on which screen is loaded. I was planning to recreate the app in Rust once I finished (Taurus framework). Now I know how I’ll approach it. Thank you!
Great video as always! Rust's enum + switch statements look really powerful, but is there a way to leverage that power if the 'states' are specified in a database (think product categories that can be specified by an admin in an ecommerce system)? I think not since all this analysis needs to happen at compile time, but I'd love to hear what you think.
ERRATA
- 4:12 Replace -> Replaced
Aaah
Panic!
What font do you use? I'd like to try out ligatures
Not an error per se, but integer types have their min and max values as associated `const`s, so you can write `u8::MAX` instead of `std::u8::MAX`
4:13 shouldn't that be "REPLACED" instead of "REPLACE"? not nagging, I love your videos and found an excuse to leave work early when I saw the notification for this one!
Damage is a transition that changes Mario’s state from Fire to Super, from Super to Mario, from… hold on, I need to compile this to make sure I got them all. :-)
I’m going through “Writing an Interpreter in Go” (Thorsten, 2018) but instead of Go I’m writing the program in Rust, translating the code as it’s presented. The Rust version using enums is *so much cleaner* then the class based system presented, and I get to skip whole sections when I realize that he’s implementing something that I already have for free. I’d highly recommend the exercise.
I am following the same think as well. Do you have a repo online?
Ohhh I have this book, I might go through it again with this approach
The hard part w using Rust on that book would be memory management. Unless u don’t care abbot it and just Box everything until ur program quits
Link to repo 🙏🙏
Oh yes, I have done something similar with the "Crafting interpreters", which code was written in Java. functional style with variants to represent AST nodes and pattern matching made it so fun
This is my favourite part of rust! As someone who works within robotics, representing state and transitions is vital, both for safety and for ensuring confidence that the system will be in well defined states for the lifetime of the program.
With so many other languages, a small change in the represented state would correspond to an unknown amount of work to correct the system behaviour elsewhere, as you just don't know that it's wrong until you reach that part of the code.
Yes! I can't even LOOK at python anymore without thinking I'm going to break it by doing a tiny fix
I love rust for this! Do you know of a way to program FRC with it?
So, do you use Rust in robotics?
@@ImaskarDono Its not difficult for UR. Just rewrite or adoptate xmlrpc wrapper to rust.
I dont use it now, but it can be great.
@@cotton_flowers I have an itch to port WPILib to Rust but I find it nontrivial to essentially rearchitect the whole thing to not use inheritance and such
As a university student looking for an excuse to learn Rust, I want to thank you so much for making concepts in your videos easier to understand!
My pleasure!
Oh my gosh that's exactly what I'm doing
@@Thomas-gi9vy me too lol
here's your excuse: to become a better programmer (not just coding and crossing fingers) while also coding in language that put zealous concepts to extreme and was built around them
OOP inheritance doesn't model reproduction unless something is cloned.
Your long search for a good state machine example was certainly worth it! Not sure how you just casually looked at the super mario world manual though haha, but I'm glad you did! Thank you as always for your great content!
Ha! yes well, though I have MANY retro computers and consoles, I admit that manual was PDF, discovered by the youtuber linked on the slide - though he didn't go into it! I thought I would :-D
Having learned Haskell just before Rust, enums felt just right. And since now having spent a lot more time in Rust, I always feel good when I use a nice match expression! Thanks Tris!
Nice! love a nice strong type model, aws sdk is like that for rust too, lovely!
This was my path too. This is the way.
When I was first starting to learn Rust coming from Python I was struggling to see the benefit of enums, but then I realized you HAVE to use enums to represent certain data structures. At first that felt restrictive, but then I realized that restriction relieved so much mental burden I'd have otherwise spent in Python thinking about the structure of the program just so that I, the one writing the program, would be able to understand it.
In Rust more than any other language I feel like I can just DO things, rush them even, and the compiler is like R2-D2 was to Luke Skywalker, guiding me and filling the gaps I'm unable to fully grasp in my limited human working memory. When my code does start to get unwieldy, refactoring isn't some mess where everything becomes more confusing, often as a program grows it seems little slots arise where you know a certain type (like an enum) clicks in like a puzzle piece.
My two cents of advice to people who want to learn Rust coming from more OO languages:
1. ```struct != class```: Be careful with Impl block methods and the "self" keyword. Structs are purely data and methods are syntactic sugar for functions, as they always are, but this relationship is much closer to the surface in Rust than it is in other OO languages and you'll hit some frustrating roadblocks if you accept "struct == class" at face value.
2. Avoid Traits except where the compiler tells you you need them or an API requires them. (i.e. #[derive(Clone, Copy, Debug)] ). As you learn you will find places where they fill a niche, but shoehorning them in as a replacement for c++ templates/js interfaces/python protocols is a recipe for unreadable spaghetti code.
3. USE ENUMS. ALWAYS, ALWAYS, ALWAYS. They are fucking awesome, as this video handily outlines.
Great advice, thank you!
In my experience enums are not the medicine for every problem, i used to overcomplicate some programs since i was so amazed by enums too, that i felt i need to use it everywhere. But later i realised what a mess i have created
Hey since you describe yourself as a guy who transitioned from python to rust ? How was it ? Was it that difficult ? I am a pro in python. Will I be able to learn rust faster ?
Can you explain a little bit more about what you mean not to see structs as classes? What roadblocks have you seen?
One of my favorite UA-cam channels even though I don’t really watch the videos. It’s just something about the overall style and personality that bleeds through them. There’s no filler and there’s no commercialism. It just feels like a sincerely enthusiastic person trying to tell you something clearly that actually is of interest. It definitely hugely has led me to be sure to check out more Rust whenever the opportunity is there. I still hear his swinging British voice in my head when I try it, like, “In Rust, all you have to do is read the compiler message carefully.”
Thank you so much! Did you know I also make an audiofiction podcast about AI, tech, and mental health? I'd love to know what you think! ua-cam.com/video/p3bDE9kszMc/v-deo.html
Season 12 just dropped this week!
no commercialism?
I love enums, they're a really nice way to represent "choices" in the type system, rather than writing ugly if statements that try to verify everything with a bunch of bools
Right! And the compiler knows what you mean too!
That Mario example is what I've needed for months! That's so good. And tying it to the actual code, chefs kiss. I finally get state machines, and I have a tangible idea of how it can be made. Thank you!
Thank you!
I pretty consistently worry that I am watching your videos too soon, having barely started with Rust, and the elegance of your explanations prevents brain fog.
This is my video for you ua-cam.com/video/2hXNd6x9sZs/v-deo.html
its not you, its too dense and too fast. My 2 cents
One of my favourite applications for state machines is fighting game input parsing. In a game like Street Fighter, a particular special move might require a sequence such as down, down-right, right, punch. But the games build in a bit of leniency, so your timing doesn't have to be exact. A great way to read the player's inputs then is to create a state machine for the special move, feeding the last several frames' worth of inputs through it. It makes it easy to define leniency as well as failure states in precise terms.
Yes! A classic example.
As someone who is currently learning rust, i just finished the chapter on enums from “the book”, and i was able to understand and comprehend whats going on, hats off to you for explaining a what seems like complicated topic in such an elegant way !! This was both a useful rust video and an explanation of how machine state logic works! Amazing
My pleasure! Enums aren't complicated, are they, but they are unfamilir!
I haven’t started playing with Rust yet but if the amount of stuff I’m consuming about it is an indication it’s going to happen any day now hah! I will say though that “Enum”s are a bit scary for me, I’ve been burned too many times. I’m not sure I can think of a single time I’ve used an enum in a language, metalanguage, db, framework, engine, what-have-you and *not* lived to regret it. I probably wouldn’t have even thought twice about it had they gone with another name :P
Your videos spiked so much interest for Rust in me that I ditched the plan for learning Golang. Reading the Chapter 10 in TheBook now.
YES! Check out my rust playlist starting with ua-cam.com/video/2hXNd6x9sZs/v-deo.html
I don't know Rust but it's really fun pausing your videos on code snippets and compiler errors, trying to figure out what it means and being successful almost every time!
Sounds like you're learning! :-)
You might find github.com/rust-lang/rustlings really works for you
I've instinctively tried to use this in other languages, only to find despair. Rust is just so fantastic.
Can agree, after getting comfortable with rust's enums I tried going back to typescript, but they had limited enum support and it just didn't feel the same as Rust.
There is no going back, welcome to the cargo cult!
Yes, I write typescript at work and rust at home. It’s makes my job so miserable when try to use a worthless typescript enum….
I wish they’d just remove them!
FYI, you can use sum types in typescript as well. It's not as clean as in rust, but you can represent the cat example pretty well there. They are just not called enums.
What typescript doesn't have tho is an equivalent to the match expression that makes the enum feature so powerful in rust.
Interesting to see a nice algebraic type system and exhaustive pattern matching outside of a functional language. This was always my favorite feature of F#.
Rust is actually a hybrid functional/imperative language, it's not a pure functional language. I think this is part of it's genius, and I see this everywhere in the language: Sensible defaults, but with escape hatches for practicality. (like the unsafe system)
Rust is pretty heavily inspired by function programming. Initial compiler was written in OCaml :)
Great as always! I'm currently writing my Bachelor's Thesis and I have utilised Rust for a lot of the implementation, and its growing more and more on me, thanks for introducing me to this wonderful language :)
Wonderful! My pleasure, good luck with it all!
I love algebraic type systems so much!! Haskell made me fall in love with them. I love this!!
Rust is applied Haskell!
Gotta say, this is one of the first few times I've seen a truly interesting and relevant sponsor product
My hope is that I'll only need to get this kind of sponsor - my experience with the Big Sponsors (squarespace etc) has been very ick
On the topic of making invalid states unrepresentable and normalized representations of data, I recommend the excellent essay by Alexis King by the title "Parse, don't validate", it's in haskell but I believe its still relevant enough.
Great vid btw :)
Ooh thank you, will read! Love haskell, for me Rust is haskell snuck into the cool kids party in C's clothing (wearing Lisp's shoes)!
I really enjoy the idea of modeling states and find it far more intuitive as a developer than OOP (especially the make many classes just to inherit down to the one I actually use). I am sure we can get around this by simplifying but my mind immediately tries to explore the edge cases where it's difficult to model the states directly with a finite state machine - such as programs that might simulate complex biological systems. It certainly doesn't abrogate the utility of representation by states but it does bring into view some interesting ideas of more fluid states of giant matrices of floating point numbers.
Yeah, we're collectively waking up from the OOP dream as an industry I think.
you are a real teacher. I must say i watched a lot of rust videos but only you were able to explain what actually rust enum is.
I'm so delighted, thank you for saying so :-)
Amazing video as always. I have kind of thought of this: I have recently had an OOP design patterns course and a functional programming course and I thought:"there must be an easier way of implementing these patterns functionally". Turns out you can indeed
First class functions + sum types = all patterns!
C++ has been learning a lot from Rust, and this kind of enum has been implemented in the Standard Template Library as std::variant. The match described here would be analogous to std::visit executes a function on the current state of the variant, and requires that the function being executed has a valid overload for any of the types declared.
C++ learns from *every* language, and that's kinda the problem, right? :-D
Someone commented on one of my videos something I think of often "If you almost never add features, you get C, if you almost never remove them, you get C++"
Great video as always!
Recently had to do a project that would need to be super reliable as it would fetch data constantly, and match arms to make error handling have just been great. Together with loop flags the code just looks clean is super readable
fantastic!
// personally I aim to live up to std::u8::MAX
8:27
Great motivation!!!
I've started my training by eating more XD
@@NoBoilerplate My training is to do 50 squats every time I go to the toilet during production xD
@@NoBoilerplate psh, I'll just use a gameshark
As soon as I tried out enums and match in rust. I completely fell in love with the language once again. And it's intuitive, everytime I use emus and match statements, it FEELS very familiar, yet, not.
Brilliant aren't they!
04:14 nice thing about objects is:
`a.push(1)` is shorter than `push_to_array(a,1)`
most other stuff is obfuscation
like my example of itsame.collect() ? That's not object orientation, that's method call syntax, every language has that! :-D
@@NoBoilerplate> like my example of itsame.collect() ?
yes
> every language has that
C doesn't have that
@@RandomGeometryDashStuff function pointers
@@mitrabeastyes but that takes up extra memory
@@RandomGeometryDashStuff just as vtable does
This is very informative. I've always struggled with data modelling, in rust, in particular. Will be saving this for many rewatchs. As always, thanks for the quality content.
Fantastic!
That was great, gave me some inspiration for a state modeling section of an embedded program I'm working on. And I really appreciate the no-fluff, information-dense, focused approach on this video!
Thank you!
This video is just amazing.
Coming from python to rust would be impossible for me without this channel!
Right! I come from python too!
In the 1980s and 1990s, there were many programming language designers working on OOP (also functional programming), such as Luca Cardelli, who was involved in the design and development of Modula-3 and ML, Philip Wadler also worked on Java and Haskell. We can see that the history of OOP and FP is closely related. However, due to the excellent marketing techniques of Sun and Oracle, some people later believe that Java has the sole right to explain OOP :P
Amazing video! I’m just getting into rust, coming from an OO background and this really clarified some things for me. Thanks for your work!
My pleasure! I hope everyone gives Rust a go, have you seen my whole Rust series? I'd start with ua-cam.com/video/2hXNd6x9sZs/v-deo.html&lc=Ugy1Nxse098tA5xqmTB4AaABAg
Really cracking video. I'm just starting out with Rust, approaching as a full reboot of my understanding of programming. I found the video very helpful. Great example with Mario..
I'm excited for you!
This is one of the most underrated features of rust. Programming states is hard and rust makes it so easy.
LOVE enums!
Hey, i really enjoy this kind of „advanced technical rust“ would love to see more of these concepts!
Thank you for your great videos!
Thank you! I'll make more :-)
I always wonder why a lot of popular languages don't have sum types natively built in. It's like.. one of the most fundamental way to model data
It's WILD they choose *gestures at all of OO's bullshit* instead of just this one simple feature!
Hi, You have given me so many cool insights and delimited them so nicely from concepts that I knew from other languages allowing me to see what the purpose they have and what the reasoning behind them are. You have in that way contributed to how I go about my work and in turn. Thanks so much.
My pleasure Alexander! Do check out my other Rust videos for tips, and links to other great educators!
The specificity in this video was excellent, I feel like I both got an idea of the theory and a concrete relatable implementation that can scale to any size. I loved this format to present the content. Well done!
Thank you so much!
I was just watching your video on How to learn Rust and this drops, NOICE
Hope you like it!
I applaud you, sir. Superior presentation, as I've come to expect. :)
Thank you so much!
I love enums. They're the rust feature I find the most useful for actually representing the problems I need. In most other languages they are less strict, less useful or dont exist at all.
This is amazing. The amount of time I've had to spend working on edge case unit testing is exhausting.
Right! 90 percent of unit tests kines are unnecessary in rust - you can focus on integration and your program
Amazing. Every time I watch your videos it feels like one step toward being a better developer.
I feel then same as I do the research to write them :-)
Very interesting way of designing a program(and thinking about the problem). Looking forward to applying the state machine!
thank you!
Nice, looking forward to learning rust - my goal for the next weeks. I'm an experienced scala developer and was happy to see lots of familiar concepts. Let's see how far I get.
Thanks for your great, concise and motivational videos!
Thank you so much for these videos! My first language was java and I think it really messed with me for years so I never felt like I could actually make anything. OO is all I knew, so it's all that made sense. But the last week or so learning rust, programming finally feels... natural to me instead of a frustrating byzantine experience. Though I did play with C, all the memory and boilerplate I had to make and use really scared me off, making me feel stupid.. Rust makes me feel like a genius, like you said!
Fantastic! I'm so pleased for you! Yes, I feel just the same!
Greetings from New Zealand. Love your videos mate, just straight up the guts info to the ears. Thanks!
Thank you! Did you know that the latest Lost Terminal 10-episode special is set on the North Island? Just wrapping it up, I'm so proud of it, it's called "41: South".
It's a Patreon special, but the first two are public!-> ua-cam.com/video/EElC_1LbyOI/v-deo.html
I'd love to know your thoughts.
(The main public podcast that I write produce and perform is here if you're interested, also it provides context to the special: ua-cam.com/video/p3bDE9kszMc/v-deo.html)
Illustrating FSMs using Super Mario powerups was an unexpected twist. Great stuff!
You can do something similar with phantom data types, like having a type called `Container` that can be locked or unlocked. This is useful because you can enforce state at the API level, for example you, if do `impl Container { fn open {...} }`, the open function will only be usable when the container is in the unlocked state.
ooh that's COOL! Yes certainly I've seen much more clever patterns, I just showed the simplest one in this video.
@@NoBoilerplate I apologise, wasn't trying to imply that the method you were showing wasn't clever or anything, I just wanted to share something I thought was cool.
Great video! My one piece of advice is: this works in Rust because of its language-level features. If you’re working in an OO language (like I am for my day job) you may end up with something that is both non-idiomatic and also doesn’t provide the same guarantees as the Rust equivalent. I have been guilty of this and it does not turn out like you’ll want it to 😅
All the more reason to oxidize your codebase, though!
Ah, yes of course! If you're stuck with OOP, then you figure out how to best get the job done - thank you for your service!
@@NoBoilerplate thanks! 🫡 I’ll say that the most recent Java iterations have introduced “sealed interfaces” and “record classes” which, combined with some improvements to pattern matching in switch statements, actually make some of the techniques you outlined more feasible (although still inferior because it’s much more verbose and the guarantees aren’t quite as strong). It’s quite illuminating of just how badly people want these features, though
I am a old school DBA and data modeling scientist.
Your video has convinced me to look further into the Rust language.
Old dogs can learn new tricks. 😆
Wonderful! I have a short playlist of essential Rust viewing, if interested: ua-cam.com/video/oY0XwMOSzq4/v-deo.html
Thank you for your video’s ❤. They definitely have me excited and high,y motivated to learn Rust.
Wonderful! This is just what I want to hear :-)
Your videos are some of the most usefully educational on the internet.
Thank you! You are too kind :-)
The fat enums with serialise macros also make it impossible to have invalid messages in client/server communications, in particular webserver/wasm, both written in rust. No need for Ajax or other url scheme, just specify operation as enums with parameters. Compiler prevents invalid message generation, and all messages must be handled
Sum types can be simulated in languages that don't have them using interfaces/abstract base classes, and distinct types for each variant. For example instead of a single Cat class/struct, you'd have two classes: AliveCat and DeadCat. That way you can have just the alive cat implement the IAliveCat inferface (which can get hungry) and the implementation is of course up to you. Of course both of them ca implement a base ICat (marker) interface. At the end of the day, the way to correctly model a domain in your programming language of choice is mostly a matter of skills.
Sum types are more than that, check this out: ua-cam.com/video/sbVxq7nNtgo/v-deo.html
@@NoBoilerplate well, true. I know. I was just trying to refer to the example usage you demonstrated. Thanks for the link.
I have a question: in the Mario example, the "match" is used against the tuple (a, b) so the compiler could cover all combinations of two enums. I understand that the state machine will never reach an undefined state, but as long as the "_" (the rest of the conditions) is used in the tuple, compile can't tell whether a wrong default condition logic is given when we add new options in to the enums, am I right?
Yes, if this were a critical system, you'd want to explicitly handle all those state transitions, i saved a bit of space by not doing so.
What would be best practices of doing | representing layered state machines? Like how an app needs to handle network requests & responses, user input, and results from long running tasks?
It's all states! As I say in the video, split up your states and transitions into modules if you need bigger layers!
I know that this is a video about enum specifically, but it ended up being one of the best videos arguing for the use of rust.
Any language that has enums is a good language. I'm really confused that go didn't add them from the start, big miss there!
Rust's also go like 10 other wierd features that make me want to code in no other language, here's a playlist if you're interested ua-cam.com/video/oY0XwMOSzq4/v-deo.html
quadratic is amazing, glad to see high level sponsors
I really dig it! I hope to keep partnering with Rusty sponsors!
2:41
i have a question
what would happen if multiple branches of the match statement were true
like for instance, what if you add 13 to the list of prime numbers
then will both primes and teen will both evaluate to true?
It runs from top to bottom. So the first arm it finds that matches, will trigger it.
Match statements are executed in order, the first one that matches will be executed. For the example you proposed, that would mean that only the prime branch gets executed.
exactly
This is actually the case in my example - (mario, mushroom) is more specific, and (_, mushroom) is less specific, but because it is later it is covered by the earlier case.
This is what "short circuits" means - first to match matches
@@job4753 i assume there's also there a way to continue it?
As a fan of Rust I have to acknowledge that Typescript's algebraic types are even more expressive. You can create union or intersection types by enumerating existing types or existing values. E.g. type result = number | boolean | "error" | (mytype & myothertype). And it also does OOP the right way, by supporting structural, as opposed to nominal subtyping (just like Rust).
One issue with these union types to represent errors is that sometimes you want the success and failure types to actually be the same type, but distinct from each other. A simple example is binary_search which returns Result. Ok(usize) means that it found it, this is the index, and Err(usize) means it didn't find it, but this is the index where it needs to be inserted.
@@TinBryn Sure, they CAN be of the same type but they don't HAVE to be. Having said this, TS doesn't support pattern matching so union type flexibility remains as a hugely underused superpower. Hope Microsoft will learn the lessons of Rust.
This is such a powerful type system that no one really knows how to do a good job of static type inference/check for intersection/union types (including basic structural subtyping), even though subtyping have a long history. The first whole-program type inference algorithm for subtyping didn't appear until 2017, and further extensions with intersection/union types won't be released until 2022, there's still a lot of work to be done (look for MLstruct or MLscript if interested, which is the state of the art). So the current languages that use these features either dynamic type checking or introduce gradual typing like Typescript.
I would make NormalPost has many PostImage and when it comes to data/information modeling.
Not sure if normal forms applies really in this case, modeling a domain doesn't need to follow DB design.
Excellent video yet again. 🎉 Thanks for making UA-cam a better place.
Thank you so much!
Brilliant as always. Articulating perfectly my reasons for migrating to Rust
Thank you so much!
Great videos and info! It's really clear and concise ❤ thank you! Greetings from Cuba.
Thank you! Greetings from London!
Hey! This is again a really great video. I get that your channel is really about showing what Rust is capable to do but I am really interested in understanding the state of rust for embedded systems. As it's stands it is generally a domain lacking great resources to learn it and I think it could greatly benefit from Rust structured way to go about things and its great community focused on creating resources for other to do great things. Would you consider making a video about that topic or could you recommend one? It's always a pleasure to see that you have uploaded a new video (or podcast)!
while i understand your issue, would it make sense for someone who hasnt done embedded Rust to make a video about it? wouldnt you need at least a few months of experience to be able to accurately represent the state of it?
quite right, I've not yet tried rust embedded (I THINK it's not quite there, for me, yet)
Thank you so much! I am afraid to say that though I'm really interested in embedded development with Rust (I talk about it enough don't I?) the tooling isn't quite there yet.
When it is, and I can build some non-trivial projects using arduino etc, you can be sure I'll do a video on it!
@@laundmo Thats absolutely right, though I still would have been interested in his opinion.
@@NoBoilerplate Glad to hear that! I hope the best for this area of programming. It has so much potential for home automations or other cool projects bringing things to the real world.
awesome content as always 💯 Thank you so much 🙏🙏
Best state machine description ever!
Thanks!
this video sold me rust. Thanks NoBoilerplate
The only thing I'd change in the example is to make collect return a Player instead of mutating the self.state field. Of course, this depends on how the wider system behaves, but one should always default to immutability unless you have a strong reason not to.
Yeah, in an early draft I used the chain pattern to do exactly that, however I decided to go for this more familiar way, so as not to hit folks with too many new patterns all at once :-)
That is not a good idea. To see why, consider what happens if you store Player as a field in a struct. You can't just take it out, shove it into a function and then pluck the hole with the return value. You would have to take the overarching struct apart and put it back together to do that. Taking shared reference is preferred here, as it lets you modify Player in place.
The Rust API guidelines discuss that in the Type Safety section, Non-consuming builders and Consuming builders. They say taking &mut self (i.e., non-consuming) is preferable when you can, but if one of the methods needs to take ownership, then all methods should take ownership so you can still chain them. The standard library has examples of both cases, e.g., std::process::Command for non-consuming and std::thread::Builder for consuming.
i think your channel is my new fav
Thank you! :-)
You can make error state unrepresentable in every good language (I often do it in Java), the reason we have so many IFs is because we are writing client facing applications that have to handle bad input, you can't make 400 Bad Request unrepresentable.
my favourite web frameworks in Rust, Poem-openapi, accepts client input in terms of data types, validated automatically by the compiler, not manually with ifs. It's wonderful!
(docs.rs/poem-openapi/2.0.26/poem_openapi/)
Your data model (using enums and structs, just like in this video) becomes the compiler-checked contract between your client and server.
Though this video is about fast learning, I am watch it at 0.75 PS and on repeat mode. Every word is a diamond. Tx
Thank you so much!
_"You can forget gang of four patterns..."_
But, they are forever etched into my smooth brain.
If you have first class functions, you don't need most of the GoF patterns! www.defmacro.org/2006/06/19/fp.html
@@NoBoilerplate After using them for much of my education and most of my career - it's hard letting go.
Stockholm syndrome, I suppose.
@7:00 Why would you use i32 for id, when the db likely uses u32?
Algebraic data types are great for expressing data models and are understandable even by the business people. Together with Typeclases one can get closer to expressing the “real” world concepts.
Absolutely! Rust's Enums and Traits get me very close to the Haskell dream
It's interesting that you bring up the ORM vs. DAL divide -- I have personally not once ever heard of the term DAL before, and even after googling it a little, I still fail to see how it is really an opposite to an ORM in any way.
Great video! I didn't necessarily think of rust enums as being so helpful at preventing invalid states
(btw, at 8:37, I think std::u8::MAX is deprecated :))
Ah yes, thank you!
This video was *awesome*, thank you!
I thought the quip about tables existing long before and after OO was maybe too witty to be true and a quick google had me wanting to a “well actually…” comment. Those suck and a little more googling showed that the history of these things is murky and entirely dependent on how you chose to draw the lines. I also don’t think leaving that kind of comment even works since it’d comparing very different “types” of things; the coining of “third normal form” v. the coining of “object oriented programming”: one hasn’t changed since 1971 (3NF) and the other continues to “evolve” since the initial coining around the same time (maybe “mutate” is a better descriptor!).
No matter what, your point works very well :)
i believe tables existed long before digital technology, even long before most analogue technology
This was what I was thinking. Babylonian trader's ledgers etc :D
Rust's Enums make me dread working in any other language - they're just so powerful and I wish that the equivalents in Python and TypeScript were as powerful. It's technically possible to do it in both, but Rust just makes it so nice to work with.
Haskell's got them :-D
Does Elm? I played with it a little bit and really enjoyed the (what they call) pattern matching
@@efkastner yes elm also supports Algebraic Data Type
This is one of the videos I'm gonna save for future reference, or as a quick link for friends who ask me "what's so great about rust?" Really well explained! I have a minor question about the very first point in the video about sum versus product types. Can't sum types be represented in any OOP language using either interfaces (and implementing classes) or subclasses with different attributes? Obviously you don't get things like match statements, which are absolutely lovely, but is there anything else I'm missing about why this is something that OOP languages can't do? Thanks!
OOP languages can do sum types. If your OOP language supports downcasting and enumerators, than you can implement sum types quite easily. The issue is the boilerplate code you need to make it work. It's usually not ergonomic nor foolproof to implement sum types in language that lacks first-class support for them.
Exactly, and because they're not first-class citizens, the compiler is not optimised to rationalise about them.
It's like saying you can build strings in any language that supports integers! :-D
I know this isn't the topic of the video but it was only a week or two ago that I was saying that I didn't understand why nonone had made a spreadsheet app that supported python in the cells.
Seems so obviously brilliant
I know right? Really nice guy too
Wooooooooo! New no boilerplate video!
Woop!
4:36 Wow, it improves all things I hate about ms excel. Basically excel was created for non-programmers and that's why it's extremely unintuitive for me to do nontrivial things.
Don't knock excel, the world runs on spreadsheets and macros! :-O
Why is there an & before self.state in the last example? Isn't self already declared as &mut?
The error if you do that is "cannot move out of `self.state` which is behind a mutable reference"
It's not exactly the same, and it's not completely there yet, but Java is working on bringing this style of programming to Java with the features: sealed classes, switch expressions, and pattern matching in switch. They call it "data oriented programming" and it's one of the over-arching goals of Project Amber - minor improvements to the language where the sum is greater than the parts.
If you almost never add features, you get C, if you never almost never remove features, you get C++.
Somehow, Java's not learned this lesson XD
Do you think Rust can implement something like Polars for NdArrays? I personally use Polars and Xarray for Climate Data Science and we really need the performance bump that Polars gives us for GeoSpatial Data?
Certainly! crates.io/search?q=NdArrays
The cat example is a bit dark 🤣🤣🤣 Great video as always.
thanks! XD
How does this compare to Kotlin’s sealed classes/interfaces with objects and data classes, and pattern matching with `when`? As far as features go, they seem to be equivalent. Is the only difference performance?
The key thing for me is that enums (ie sum types) existed in Rust from the start.
The whole language is built upon enums (such as the Result enum) whereas it's just one of many options available to you in Kotlin.
Building your language on Sum Types is MAGICAL!
What would be the challenges faced if we were to do the Mario example in Java using the state pattern? (see "gameprogrammingpatterns" by Robert Nystrom). Apart from the lack of pattern-matching, it seems like it would be very similar. But I guess the Mario example doesn't use tagged unions, and if we had to, the java code would be more complex
From a pure technical point of view, Java's classes are not zero-cost abstractions, they incur runtime penalties to use.
Rust's impl blocks are just functions, no overhead!
From a design point of view, I can only suggest that thought you CAN do almost anything you like using classes (see the GoF book) if you have first-class functions and sum types, you don't need to.
I have been looking to model some more complex things. For example, finite groups. So far, embedding the group into some numeric type has worked best for me. This isn't very pleasant because there are usually many possible numbers that do not represent elements of the group. I want to use a system like this, but there is no way I can just write out all of the operations by hand when working with something like the 10th alternating group (10!/2 elements). Is there an intelligent way to do these kinds of things in Rust?
Yes, this is what a macro would be used for - programmatic building of enums - you can either make your own (I'd use macro_rules) or see if someone has made this for you, by searching on crates.io
@@NoBoilerplate Thanks So Much! This is very helpful.
So I wonder, if in rust you put structs in your enum, what will be the size of an enum value? Does it work like C union but safe or is it gonna get big?
I'm not much of a low-level programmer, Rust is my first such language (though I'm having a great time!) but The Book has the answers you seek doc.rust-lang.org/reference/type-layout.html
@@NoBoilerplate Thanks. It seems like it does work like C unions after all
Tbh I'm not low level coder myself, it's just good to know information. Knowing this I could add to your example from video another one about implementing blackboards.
Common technique in game AI, basically a key->value dictionary where value can be multiple different types (e.g. Vector, int, pointer) so you don't want all this stuff to add up and make struct too big. You also don't want virtual functions here, obviously.
Rust actually would help a lot here by ensuring that I would never read value of a wrong type while keeping struct size reasonable
Wish I could actually use it, but Unity is C# only :(
Yes, Rust enums are by default represented as a pair of discriminant (a classical integer enumerator) and a C-like union of structs. Sometimes the compiler can do layout optimizations, like hiding the discriminant in padding bytes, or using invalid values of one variant to represent another.
As a rule of thumb, Rust enum will have the size of its biggest variant + one or two bytes for the discriminant.
I have question, I'm designing an operation that outputs Option. When HashSet has zero elements, it should be None instead. Is it possible to have compiler check for the code if it can create empty Set? In this case, Some variant with empty set would be invalid state.
This would be a more advanced usage of Dependent Types (en.wikipedia.org/wiki/Dependent_type), a feature Rust does not have. Generally, the feature is that you can make the Type depend on the value. but this leads to a lot of complexity in the type system which may make it harder to be productive or understand code.
You declare a new type that has Option as a private field. Then you build an interface around it that guarantees the inner field is either None or Some("nonempty" HashSet). The constructor could look something like this.
struct MyType( Option );
impl MyType {
fn new(v: HashSet) -> Self {
if v.is_empty() {
Self(None)
} else {
Self(Some(v))
}
}
}
Accessing the inner hashset mutably would be an unsafe operation. A safe alternative would be a method that gives a "borrow guard" type, that derefs into &mut HashSet, and fixes the parent MyType in its destructor.
Laund and Kohu have answered very smartly.
If it were me, I'd redesign my whole application around making my data representable around enums. Not always possible!
Fascinating. As a mostly .Net user I'm a little envious about this.
rust curious here enum's are elegant, not a fan of ORM but how do you map your tables to enum's without boilerplate?
Take a look at the sqlx crate, there's no mapping needed!
As always lovely video!
Thank you so much!
I’m building my first desktop app for my FIL’s work. For simplicity, I’m writing it in Python using a custom TUI package. The core interactivity of the app involves mapping key presses to actions, which depend on which screen is loaded.
I was planning to recreate the app in Rust once I finished (Taurus framework). Now I know how I’ll approach it. Thank you!
Fantastic! If you wanted to build a Rust TUI, there's a fantastic framework called crates.io/crates/cursive
But Tauri will be great too!
Great video as always! Rust's enum + switch statements look really powerful, but is there a way to leverage that power if the 'states' are specified in a database (think product categories that can be specified by an admin in an ecommerce system)? I think not since all this analysis needs to happen at compile time, but I'd love to hear what you think.
databases often have sum types too! I am certain posgres does, and you can then use SQLx to compile-time verify your queries! lib.rs/crates/sqlx
@@NoBoilerplate I'll look into that, thanks!