ERRATA - While Clippy's nursery group is very useful for running locally, it's got lints that aren't ready for production use; they can have false positives or plain wrong suggestions. Don't use them with --fix.
@@SkyyySi It likely is a bit wonky, I have to do a lot of hacking around, and I have no customisation of reveal.js as used in Obsidian. Could you give me a time code for an example?
I think it was a bit too short for such huge subject, but I will have a few things to search for. 🙂 PS: The only channel where I don't even skip the "sponsor" section.
@@NoBoilerplate hopefully testing in Rust can be a miniseries of its own. I work in automated testing, and suffice to say I'm heavily invested in the topic 😁
So glad I found your channel. The accurate, no-nonsense, logically argued information with some interspersed humour and little reminders of sanity checks has really helped. Thank you.
Great video, as always! I'd say it's appropriate to compare features of Rust only with those found in other compiled languages with strict static typing (Go, Java, C++). I think dynamic languages serve such a fundamentally different purpose, that they, as tools, are not even neighboring Rust. They are completely different, with their own use cases and advantages, the main one being interactivity.
Thank you for your comment. As a python developer of 15 years, I agree with much that you have said. Even with my demonstrable excitement with Rust, I shy away from recommending it in data science, where interactivity is vital. Though I tell people to try Julia rather than Python, these days. That being said, a core thesis of my Rust series is that Rust CAN compete in projects that were traditionally dominated by dynamic languages (ruby on rails, python's django etc). This is because what interactivity you lose through not having a runtime shell, you gain through rich compiler feedback. And compiler feedback is FASTER. Further, repl driven development is optional, in Rust, compiler driven development is MANDATORY. More of my thoughts in this video ua-cam.com/video/4dvf6kM70qM/v-deo.html
@@NoBoilerplate Makes sense! What do you think about Julia's industry adoption and applicability? Does Python's library ecosystem trump its shortcomings as a language of ML and Data Science? And so no other language will see widespread industry adoption in these fields in the near future?
@@user-mt6lk8ld7w I think Python's GIL (which is confirmed NOT replaced in 4.0) is a HUGE problem for parallel computation, python can't stay relevant dragging this ball and chain around.
I'm working on a sqlx-like client for surrealdb, and hearing about the fuzz library was exactly what I needed! Perhaps my dreams of compile time query testing are within my coding abilities after all
I’m moving from Python to Rust, and it’s been quite hard. One cannot simply try to re-write everything with the same logic. For example, in Rust the Builder Pattern is really great for constructing complex objects, and makes rust far more readable. In python there’s very little use case for this pattern except for testing purposes. Keep up the good content! (This channel helps me a lot in my from python to rust journey).
Well done, as someone who also is transitioning from Python to Rust, my advice is: Keep going! I make my rust videos to show that there's INCREDIBLE features that you simply can't get elsewhere. Have you seen my other videos? Good luck!
@@NoBoilerplate Yeah, I’ve been following your channel! And yes, Rust has really nice features such as the borrow checker which is a miracle. It’s far more complicated to write bugs in rust than python.
10:52 just saying about the fire alarm pressing the button only tests that it can make noise while you should actually make a little fire (that has smoke, like burning some paper, not just using a lighter) to test the entire system
Ah, an integration test! This is actually what companies do, don't they? They've got these portable smoke machines on poles that cover individual sensors? The quote is from the Pact website, fyi
The python example isn't fair in my opinion. You can use type annotations in python and every halfway recent lsp will shout at you when passing wrong types and I think they are easier to read than the rust ones.
As a professional python developer, I too am baffled why Hypothosis doesn't use them! This is the problem with optional typing: People can ignore them, as much of the python ecosystem does :-( I do love that it's possible, though! I write interfaces for all my code in mypy. I don't know how I'd stay sane if I didn't!
Python doesn’t enforce the types, though, and many developers still don’t use them (including some library developers). You can’t prove that a variable will be the type you think it is going to be in Python, even with type annotations.
My main problem with Python is that the type system is immature. There aren’t nearly enough features to allow me to express the nuances of a dynamic language, so I often fall back on Any and cast, which create errors that are allowed to ripple throughout my codebase. TypeScript solves this by giving you an extensive feature set capable of describing almost anything you might want, eliminating the need for any and casting, but Rust solves it with a deep understanding of the code and all possible states, forcing you to use explicit runtime verification for things it can’t understand like boxed traits (like Any)
The 'meditation for coders'? Oh I'm absolutely returning to those kinds of videos. I am going to start phasing them in slowly over the course of the next few months. Next video isn't about Rust again, it's about AI! I'm going to make more videos this year, would you subscribe to a patreon if there were more such videos? I'm trying to work out how best to set up my channel...!
@@NoBoilerplate Well, I'm sure excited to see you cover more things with the same approach you have right now. Things like comparing Rust to other languages, or providing ways to do things in rust that are typically done in other languages would be good, too (like the Yew video). I believe making different thumbnails would help the channel, too. Above all, keep the technical, fast style we all love.
I'm no programmer, but this quality of delivery is very impressive. I think your videos and community engagement is something other project could learn from. "No Boilerplate" and "N-O-D-E" really are talented people.
N-O-D-E is a huge inspiration for me, as you can very likely tell, I'm honored to be mentioned in the same sentence. I have started producing technical, but non-programming videos this year, stay tuned! (An old example is ua-cam.com/video/4rWwBYjoiJ0/v-deo.html)
Great video - coming from python, I found this very helpful. I hope you cover the sponsor's architecture more in the future since it seemed very interesting with the combined microservices, Python and Rust.
Thanks for another great video! There are some good crates for mocking complex objects, like *mockall* for struct and trait. Something else I want to try, to complete tests on libraries: *static_assertions* (it's apparently not updated anymore, hopefully because it's not necessary) or *more-asserts* . The one thing that bugs me with "Cargo test" is the lack of option to test combinations of features, and *cargo-all-features* does it.
So you could take a C library, wrap it in Rust's type system based on the documentation, and then use fuzz to automatically fuzz the C library with all supposedly valid inputs based on the type information your provided via Rust.
Golang tests are magic, project of any size will compile within few seconds and test run takes few seconds, while nodejs rust etc take time to compline decent size project and then run those tests as well
I really like Go! No arguments that it's got fast tests, but I'm not so sure they're magic other than that. Back in 2020 I evaluated three languages (go, haskell, and Rust) to see which I would focus on, I know it could be any of the three. I went with Rust because of the astonishing number of features packed in that other popular languages just don't have. Rust's compile times are slower than Go's because they're doing HUGE amounts of work for you that Go can't do due to its simpler type system. You might not know this if you're new to rust. Two of them are Rust's Macros and the Unsafe system, I did a deep-dive on both here, and I'd love your take, if you have time: ua-cam.com/video/PuMXWc0xrK0/v-deo.html
I'm in a tough spot with rust. I can't really think of any technical issues with it, but I really just don't enjoy writing rust code. I feel like I'm in a constant battle with the compiler and it never gives me anything back for my hard work. I get guaranteed memory safety but 99% of the issues with programs I write aren't memory issues. I think saying it is the future is probably valid and more power to the people who want that, and I probably should too, but I'm not sure it's a future I want to be a part of as it just feels less enjoyable.
Rust is extremely different isn't it! No inheritance, new syntax for lifetime annotations and borrows, and the compiler hates a lot of the standard normal patterns we've been using for decades. I know how you feel. Back in 2020 I crashed out of learning Rust twice. First time due to multiple string types (Haskell's biggest mistake made again, I thought to myself) and second due to lifetimes. At the time I had a great mentor who picked me up and helped me back on the path. Most people don't have this. This is why I made my Rust series. Yes Rust is very different from what you're used to. But that's the point. The difficulty curve is because you are learning things no other language can do. Like a bicycle vs a motorbike. Did you see my video on this topic? ua-cam.com/video/4YU_r70yGjQ/v-deo.html
@@NoBoilerplate I have yeah (I think I've seen almost all of your videos). I imagine most of what I have an issue with is an issue of time and experience. I'm primarily used to garbage collected languages like C# and Java, and memory unsafe languages like C++. Both of them debatably give you a little bit too much freedom is how you solve a problem, and that can result in bad outcomes. However, I feel like Rust leans just a little bit too far the other way for me, as there's been a couple of instances where I know my code is safe in context but the compiler won't allow it because it's not safe in _all_ contexts. Potentially I should be aiming for all contexts being safe as that's safer for any future changes, but it does feel like this constant extra overhead as I'm writing it. Again though, maybe that's an issue of experience and I just need to write more rust code 😆
@@moff181 It's an overhead at the beginning, absolutely no doubt there. But as you build more and more complex apps, it saves you more and more. I like to say that in other languages, easy things are easy and hard things are possible, whereas in Rust, easy things are possible, and hard things are easy!
Very good read, thank you (it's here for others blog.burntsushi.net/unwrap/) I totally agree with the burntsushi's point that "I think it is a good idea to generally recommend the use of expect() over unwrap()." Where we differ is I don't agree with what they go on to say: "I do not think it’s a good idea to ban unwrap() completely." Their argument against .expect() is 'line noise', something that personally I'm uninterested in optimising. I love long variable names and verbose documentation! Thank you very much Frank for linking me to this, it's improved my understanding of panicking in rust :-)
@@NoBoilerplate Of course, different views on this - mostly stylistic - question are possible; as long as one isn’t advocating to never use unwrap OR expect either (or any panic! either, perhaps), it IS mostly a question of style I suppose. AFAIK, there’s also an interesting historical context here, in that unwrap used to be way worse in older Rust versions in that you couldn’t determine the location of the unwrap that panicked from the panic message. Nowadays the panic message DOES include a source location, but before it did (I’m not totally sure when that changed, would need to look into this more to figure it out) there was a very valid point in avoiding unwrap and using expect instead merely to make the error messages objectively more useful by becoming less ambiguous. In my personal opinion, the more relevant aspect of using either of unwrap or expect is that there’s a comment explaining why the operation is expected to never fail, in all but the most straightforward case. Whether or not some aspect of this reasoning is copied into an "expect" error message string, I wouldn't particularly care. I also noticed that burntsushi wrote in their blog post that they don't use unwrap for avoiding error handling in prototyping. If one never even temporarily incorrectly uses unwrap anyways, the situation is different from when one choses to use unwrap as a linted-against tool in temporary WIP situations. If there are no incorrect unwraps in the first place, you don't need the lint, and if you don't need the lint, you are free to use unwrap "correctly" (in cases where the alternative approach/convention would only have accepted "expect).
Can one also do formal verification with Rust? I do that with Ada/SPARK and it's great because there can be absolutely no runtime errors since you essentially prove your program. As systems get larger, edge cases get increasingly harder to detect due to the increase in variables. Testing can only cover so much cases and if you have more variables, there are exponentially more cases.
You can tell by the wording in my video that I'm familiar with formal methods, I hope! "state space" is indeed the problem. In short, Compromises are needed, I think Rust has the best compromises between formal verification and pragmatism. Let me tell you my story. At university we were taught Z, B, and Coq. I was devastated that there are no practical verified languages, even using these methods you must translate your model into (usually) C++ or Java. My top-voted SO question gives you an insight into my mental state at this time stackoverflow.com/questions/4077970/can-haskell-functions-be-proved-model-checked-verified-with-correctness-properti So I went on to learn Haskell, as it seemed to provide much of the confidence in my code that I wanted, while being a very robust, well-supported language. However, the esoteric syntax and purity system, while being exactly what I wanted, has kept it back in popularity, which meant I never was able to persuade any team to use it. Fast forward to 2020, and I was STILL sick of writing code I can't prove. Python with mypy and JS with TS isn't cutting it. I evaluate Go, Rust, Haskell, and Typed Clojure as the top-popularity languages that might have enough safety for me. After crashing out twice, Rust finally stuck. --- Can you do formal verification with Rust? - Yes. Does anyone yet do it? No, though dependent types are supported by a few crates. One of the reasons Rust excites me is that it will not take long for someone to embed Coq (or Z or similar) INSIDE a Rust macro, which generates verified Rust code AT COMPILE TIME, and transparently inserts the verified Rust code into the compiler. I imagine this could best be done at a critical function level, as we do with formal methods. I will wait patiently for such a system to be written. --- Even before someone (god I hope it doesn't have to be me!) writes this system, Rust already gives me Haskell-levels of confidence in my code, as I explained at the start of this video. I wouldn't yet build a pacemaker or an autopilot system using Rust, but it doesn't have to do that for me to be excited: Bringing industry-leading correctness to non-critical applications (like webapps and services) makes me VERY excited, safe in the knowledge that more safety is possible. I started writing my Rust series because of this excitement, a video of mine I'd recommend to you is this one, about Rust's error safety, especially the compiler-verification that no panics are possible towards the end ua-cam.com/video/sbVxq7nNtgo/v-deo.html :-)
Do not cite the deep magic to me, friend, I was there when it was written ;-) As a python developer of 15 years, I love typehints, and write all my code using mypy. The problem in this example is that the Hypothosis used decorators to do typing instead of typehints. I too am baffled why they did that, though they would ALSO have to decorate the function, so maybe they thought they'd do both at the same time? The problem, and strength, of Python is legacy. Yes you can use mypy and typehints, but most people don't. Typescript got it right - fork the language and make it the default. Though it's still optional in TS, and built on very shaky JS ground...
While the power of Rust is simply amazing, modern Python can ensure type checks with the right annotations. The only difference is in the mandatory nature of Rust typing, Python's is opt-in.
As a Python developer of 15 years, I see two problems with your statement: 1. Opt-in means opt-out for most. Typeshed has perhaps 100 libraries with mypy stubs github.com/python/typeshed/tree/main/stubs. One. Hundred. Is YOUR TEAM going to write all the stubs for everything you use? 2. Mypy and other type checkers are very limited in their scope. Rust's type checking isn't like C or Java's, or like Python's. The amount of meaning you can encode in it is nothing short of astonishing! I'd love to know your thoughts on my video about it: ua-cam.com/video/4YU_r70yGjQ/v-deo.html
@NoBoilerplate I'll admit that I don't have as much experience, and your remarks are absolutely valid. In my use case, I avoid external libraries so the lack of annotations didn't really bother me. However, I have nothing to say about the strength of Rust's algebraic type system. It is indeed much much powerful and expressive. Really the point of my comment was to poke at the python examples, which can - but mostly won't, for the reasons you pointed - be much safer, easier to document at almost no additional cost :)
@@NoBoilerplate Sure, that's one exception. Type hints are part of the dev documentation, it's easier to reference the intellisense where the types are formatted in a standard way that integrates in the editor. I'm well aware that it's not close to a full doc, but it helps a lot, compared to text in a docstring.
Man, Rust is really commited to getting all of the value out of it's type system. "Machine-readable JSON" (10:33) It's a text file, 😆 The tools showcased here seem to be pretty good advancements in web-dev and CRUD apps.
Did you see my video showcasing Yew.rs? The type system is such an incredible thing! I've known about algebraic types for over a decade by this point (I learned haskell in 2009) but I never thought I'd see it in a popular, practical language! My yew video: ua-cam.com/video/P4LMfkFLRsI/v-deo.html
Nearly nothing rust does is new. Novelty isn't what I'm excited by. Rust's compromises of what to put in and what to leave out are SO GREAT! Rust's not exciting, it's boring, as I said here: ua-cam.com/video/oY0XwMOSzq4/v-deo.html
Thank you! Yes that's a huge realisation for me. But: Sorry I wasn't clear, the line is "If more nuance is needed, then don't write an if statement, add more detail to your model." You can always refer to my compile-checked markdown to find the script! github.com/0atman/noboilerplate/blob/main/scripts/16-rust-testing.md?plain=1#L155
Right! That's just rude. My dream is to encode so much of my logic in the type system that the compiler keeps my business logic safe from my own stupidity!
Noted, might take me a while to get there. I can help now, however! Of those three, AWS is 1st-class aws.amazon.com/sdk-for-rust/ Azure is good github.com/Azure/azure-sdk-for-rust GCP lacks first-party support, though many crates are available crates.io/search?q=gcp
Rust doctests seems kind of gross. Equal amounts of setup code, no static analysis of the test, no function usage help from IDE, if there are any changes in helper functions or std then no static warnings. Is there any open source codebases which use doctest where it makes sense, and is this a pattern worth preserving from other programming languages?
Huh this is EXTREMELY bad news. I didn't realise doctests weren't compile checked in the same way as regular code, I can indeed confirm that even in vscode, with the rust packages installed, though it's syntax highlighting it, it doesn't interact with lsp. Very disappointing github.com/rust-lang/cargo/issues/6424 Perhaps it would be best to use them just for very simple uses, like an acceptance test, that rarely changes, then it can run on CI
@@NoBoilerplate Doc tests don't really interact with the LSP or cargo check, but they are run as real tests when you run cargo test. Also when I write a doc test in vscode (I'm actually using vscodium), it brings up a button I can click "Run Doctest". Once when I was browsing docs dot rs, the place where every single rust library is automatically documented, I saw code blocks with warnings that the code examples were not run. Whenever you're reading documentation on docs dot rs and see a code block, you can know that the code is guaranteed to work in the way that you would expect from what the code is. If that code block was not run as a test on that release of the crate, it will warn you about it and you can hover over the information icon on the left of the code block to see it explain that it was never confirmed to run correctly. If you want to see an example of this information icon, you can see it in the docs of Option::unwrap where the test is deliberately supposed to panic when calling it on None. There the warning that it deliberately panics is red, and if the doc test was not run, it would be orange if I recall correctly. I assume there would be an even clearer warning if the test was run but the result was not as expected.
Hi Tris, how would you feel about looking into getting your vids onto Nebula (I have no idea how that works or how you'd do it) it would be cool to be able to watch them away from UA-cam which I feel is fighting for my attention. Like there's all these little things meant to make me stay on the platform, click on ads etc.
I'm with you on that one, and you're not the first person to ask me to host off-youtube. My solution is to host ad-free, tracking-free videos on my Patreon, for pay what you like: www.patreon.com/noboilerplate I'm slowly building up the community around patreon, and currently there's behind-the-scenes posts and a vip section on my discord. I'll be adding more features this year, stay tuned for that! I'm excited that No Boilerplate is becoming more than a hobby for me, but for it to be sustainable, I have to think about how to pay myself for my time. My hope is to grow to weekly videos over the course of the upcoming year, but to do that I'll have to work part time at my day job. Exciting, but also terrifying!
You could also try using RSS. I think there is a way to addd UA-cam channels' videos to your RSS feed, which bypasses the UA-cam website altogether and just gives you a video whenever the channel uploads. I think there's also a way to hook this up to yt-dlp so that you don't have to watch the ads either ^^.
@@spaghettiking653 Ooh, I didn't know there were RSS feeds for channels, that's GREAT! Looks like it's pretty simple once you know: stackoverflow.com/questions/64232403/rss-feed-for-youtube-channels
Just because you know the schema, doesn't mean both sides will use it! It would make contract testing MUCH easier, certainly. I'd still want to run it, you can't trust external services ;-)
just checked the fuzzing option, and first line of text is, that it does not work yet :( (on x86 Windows) > Note: libFuzzer needs LLVM sanitizer support, so this only works on x86-64 Linux, x86-64 macOS and Apple-Silicon (aarch64) macOS
If you're coding on windows, as far as I understand, you MUST use WSL. This goes for all open source programming languages - the Linux ecosystem is ubiquitous, and microsoft have done an AMAZING job with wsl2 - try it out!
@@NoBoilerplate using WSL to run WINE sounds like pretty crazy idea :( If that is even possible to set up, I do not think it would be really easy. "Rewrite in Rust" It feels like there is so much I do not understand, so that is not possible with my current skills :( If you would be able to do that (especially if you would be able to offer explanations of steps taken, so I can learn from it) I would be willing to negotiate some financial compensation. The function I am interested in right now generates a random map for a game. Theoretically inputs are `seed: NonZeroU16`, `pve_pvp: bool`, and player count (pve: 1, 2, 4; pvp: 2, 4, 6), and game configuration files. Requirement would be that the rust version must generate exactly same map with same inputs (and must not be slower). If you want to reach me for details my nickname on Discord is `kubik`, or you can use email `rewrite_in_runst@simple.login.bandola.cz` (I am using simplelogin.io/ when putting an email address on place like this).
ADORE clojure, I wrote it professionally at a bank for 2 years a while back. My very first NB video was on Clojure! ua-cam.com/video/k_4rLyqQeAA/v-deo.html However, it's hampered by the JVM so much. I now get the same joy in programming I used to get in lisp with Rust. Instead of a conversation with a runtime, evaluating sexps, I have a conversation with the compiler. It's fantastic! Here's my video on this effect ua-cam.com/video/CJtvnepMVAU/v-deo.html
The main thing I found disappointing about rust tests is they can only produce "pass" and "fail" results. I'd really like to see "skip" (this test can't be run because of some hardware or environemnt issue) and "xfail" (this test failure represents a known bug)
That would be handy! But that's just a test framework feature, not a language feature, right? I imagine one of the test frameworks has the features you would like. As for hardware, I believe you would do this sort of thing by using the cfg attribute, such as here, when you don't want to run a test on wasm: #![cfg(not(target_arch="wasm32"))] Docs: doc.rust-lang.org/stable/rust-by-example/attribute/cfg.html
I wonder if PHP has taken some inspiration from Rust in the last years. Function return types and match constructs look very similar. function foo(): String {} $a = match($b) { 1 => doOneThing(), 2 => doTheOtherThing(), default => doTheDefault(), }; I'm in the process of learning Rust and it makes me respect PHP a little more seeing how it has some of the same features that many other scripting languages don't. I still want to leave PHP because PHP devs are dragging the language down, many of them still see types etc as "visual clutter" or "making the project harder to change" and I just don't want to have these kinds of arguments in my team anymore.
Maybe I'll actually start writing tests now. LOL just kidding. I don't learn my lesson until I've got something to cry about, it seems. Looks way better than the js testing I never did, though
I love your work here, but I think you're going a bit far comparing an loosely typed language like Python and a strongly typed language like Rust. Can you do comparisons against another strongly typed language, like say C#?
I'm afraid I don't know C#, I'm a python developer by trade, so that's what I compare against mostly (and JS too). Most popular languages are dynamically typed, however: 1 JavaScript 2 Python 3 Java - Statically Typed 4 PHP 5 C# - Statically Typed (redmonk.com) The real difference between Rust and all these other languages for testing is the macro system, in this case. My understanding is that C# doesn't have a lisp-style macro system?
@@NoBoilerplate I would argue that TypeScript also gives you the "superpowers" you refer to. I'm not a Python dev, but in my experience it's a similar issue with any loosely/dynamically typed language. Maybe since JS it at the top of your list, type guarding could be compared using TS. null (and undefined) are a real pain, but TS solves that with strict-null-checks. C# attempts to solve it with "Nullable Reference Types". I think the things that set Rust apart are it's capability to target so many things and do it well. Thanks for your review of the testing in Rust. But again, testing a statically typed language is much easier for obvious reasons.
@@urbanelemental3308 Rust's solution is to have no nulls - the best solution! I must insist that Rust isn't just another statically typed language, I'm writing my Rust series to explain this, I'd love to know your thoughts on this video ua-cam.com/video/PuMXWc0xrK0/v-deo.html
@@NoBoilerplate Oh I absolutely agree about not having null as the answer! I'm just saying that there has been interesting effort to help solve/reduce the problem in statically typed languages. And no question, I love your videos. You're doing an outstanding job. Yeah, macros are special! I love them. And if I understand Rust correctly, 'traits' are kind of like extensions in C#. Extensions in C# are what facilitated the LINQ library. Ultimately, Rust started with the lessons of the past and did things right. All that said. Rust = hard-mode. And I struggle with it. IMO, I think C#/.NET has made some really interesting strides that try to solve a lot of the issues with memory, performance (making unsafe code safe).
I'm never without it, even if I'm using an editor with LSP. It's part of my toolkit, which I talked about here! ua-cam.com/video/ifaLk5v3W90/v-deo.html
IMO these days the only good thing most of your videos is for is for showcasing Rust to devs who *never* tried it. Because if they had written a Rust project, they would know all of these rules and concepts already, otherwise the compiler wont compile. Your videos are good for the first weeks I began following you (when I first learned Rust and your channel was just born), but then it got boring and every one of them feels the same - just you outlining how Rust is so great, and some small tips etc. Maybe you should share more in- depth or specialized Rust topics that would be useful for current Rust devs too.
Congratulations on your success! let me be very clear: **My videos are mostly hype by design.** Most comments on my videos are not by people like you. **They are by total Rust newbies.** Some are even new to programming and are so excited by what they see. If you don't understand Rust, and someone explains it clearly, as I try to, it seems like magic. Rust is the [#19th most popular language](redmonk.com/sogrady/2022/03/28/language-rankings-1-22/), but the scale is not linear. There is a lot of work to do if we are to not suffer the same fate as Haskell. **I want Rust to achieve escape velocity.** I see my job not as a details person, I am way under-qualified for that, I point people to fasterthanli.me for details. My focus is funneling new people to Rust, getting them so excited they can't help but push through the learning curve, and then from there language sells itself. Back in 2020 I crashed out of learning Rust twice. First time due to multiple string types (haskell's biggest mistake made again, I thought to myself) and second due to lifetimes. At the time I had a great mentor (Shout-out to Alex!) who picked me up and helped me back on the path. Most people don't have this. They need the excitement of my hype videos to break through. I will eventually run out of hype topics and move on to slightly more detail. But first, there's a few million developers I want to send to [rustup.rs](rustup.rs). :-)
Everything except cpu errors are pushed to compile time in rust, which is astonishingly good! That covers every case that I care about. It's possible to segfault inside an unsafe {} block, but you have to TRY really hard to do it :-) stackoverflow.com/questions/62844652/what-is-an-example-of-rust-code-that-causes-a-segfault
My hope with this video, and others in my Rust series, is to show how the rich type systems allows you to encode MORE logic than java, go or c etc, moving more and more of your logic from runtime, which as you've said requires tests, to compile time, which doesn't.
It certainly makes me feel like a genius! Did you see my video on that topic? ua-cam.com/video/0rJ94rbdteE/v-deo.html would love to know what you think!
Wow, you're compiling some very advanced code! Those sorts of interior mutability errors happen very rarely when using some of the data types like `std::cell::RefCell` or `std::rc::Rc`. There is a wonderful video by friend of the channel Ken, at CTTM that explains these advanced types if you are interested: ua-cam.com/video/CTTiaOo4cbY/v-deo.html How far along are you in your Rust journey?
@@kamertonaudiophileplayer847 I'd be very happy to help you! Try my intro video ua-cam.com/video/br3GIIQeefY/v-deo.html or the article it was based on: fasterthanli.me/articles/a-half-hour-to-learn-rust Let me know if I can help 🙂
@@NoBoilerplate Thank you, sure it would help . If you know somebody who 's experienced with Rust internal, for example use Rust crates without Cargo, let me know.
LOVE elixir. However, the reason I'm not up here shouting about it is, sadly, popularity matters redmonk.com/sogrady/2022/10/20/language-rankings-6-22/EDCIT EDIT: Brian May explains below how type hinting in elixir isn't great
Type hinting in Elixir is currently in a terrible state. dialyzer is slow, errors are incomprehensive and typically blame the function instead of the function caller, and it can miss serious problems. Have discovered basic type errors in existing deployed code that directly contract the type hints, but dialyzer is unable to detect the problem. Result: genserver crashes intermittently because somewhere in the code, possibly as a result of an old refactor, the state is corrupted with value of wrong type. One of the main reasons I am using Rust more and more.
watching all your videos and comment section peoples talking like aliens I am trying to understand what they are all discussing but I can't because I'm a beginner in programming language
We all have to start somewhere :-) If you are a complete beginner, I recommend trying out github.com/rust-lang/rustlings - which are tiny, simple coding exercises that start out from hello world and slowly introduce the language to you! The Rust Book is also a great resource that is beginner friendly doc.rust-lang.org/stable/book/
Sorry about that! Here's a tip: Apply anyway and they might keep your details on file for when they're ready to hire outside the UK! This tip works for any company honestly. Are you actively looking for Rust work? Try applying to every company who sponsors Rust: www.rust-lang.org/sponsors Worth a shot!
Why can't Rust add function overloading or class inheritance like C++. Not only would this make life 100x easier it also would turn Rust into the most powerful language in the world
I disagree - the point of rust is not and has never been to be like c++, the point is to be rust. Rust purposely avoided class inheritance because it creates the OOP mess of class trees and method overriding, which is especially visible in java. Traits handle the problem of shared functionality already. Adding class inheritance would provide a second way to solve the same problem, which can be way more confusing to work with (e.g. types vs interfaces in js) and reintroduces the hierarchy issues of OOP. Function overloading ignores rust’s strength of clear, explicit code - combining two functions with different sets of arguments that do different things under the same name only obfuscates the code and removes the guarantee that code is easy to use/interface with. If functions take different arguments, they are different functions - let them have different names. This makes sure when you write or see a function call you know exactly what types it is acting on. Trying to use/retrofit rust like c++ won’t work because they’re fundamentally different languages - writing good rust and reaping its benefits requires you to think in rust.
function overloading is nice to have I guess, but its not without its consequences. Solving which function should be ran is far from trivial when we already have generics (which actually cover many of the use cases if function overloading). It would also completely break the powerful type inference that rust has. Rust has some things from OOP, but it intentionally avoids inheritance. Inheritance has multiple jobs, but it lacks the expressiveness to show which purpose its being used for. Composition, generics, enums and macros are all features that cover use cases of inheritance, its just split up to do each job better on its own, and make it much clearer what the intent is. Trust me, these things were very strongly considered when designing rust, and they were decided against.
@@theroboman727 I need to disagree. First of all, function overloading is required when you have different amounts of arguments for each function. Functions expose behaviour, therefore it would make sense that two functions with the same behaviour should have the same name. With Rust however you are supposed to be even more verbose than you already are. Secondly, OOP on it's own is very powerful in some places, and sometimes even better than the way Rust does things. For example the best retained UI frameworks we have seen are all OOP. And it doesn't seem like anyone in the Rust community has found a better way to do so. Also supporting OOP in a similar way C++ does would allow for better interop. The current interop with C++ is very lackluster. Missing simple OOP capabilities is also the reason why we will (for now) never get any Xaml capabilities in the windows crate. Why can't we have both? Nobody wants asks for a stupid GC or exceptions but rather just simple inheritance capabilities. Rust would be the perfect language to have that. People don't understand what goals programming languages have in general. They are supposed to make life easier, not harder. Rust makes it way too hard to write such stuff, even Assembly would do a better job at that point.
@@CXCubeHD "First of all, function overloading is required when you have different amounts of arguments for each function" Its not. The first approach you could take is that you can take a slice &[T] as an argument and treat that as the parameter list. Or you could use const generics with an array like this: fn takes_n_numbers(args: [i32; N]) { Really the only difference to variadics is that you have to write extra [] The third approach is to use a macro, not a function, for what you want. This is why println! is a macro "therefore it would make sense that two functions with the same behaviour should have the same name" They aren't exactly the same though. The fact that you had to code a second overload shows that there's something different going on, even if its minor. "With Rust however you are supposed to write even more bloat" what do you mean? the amount of code with function overloading vs without is the exact same, just that with function overloading youre putting all of the functions under the same name. The only difference when youre using the function is going to be that you write `function_x_with_y(x, y)` instead of `function_x(x, y)`. That's not bloat, you just need to be slightly more explicit about it. Yes, its a tiny bit more boilerplate that may not be necessary depending on the situation, but that is insignificant compared to the consequences of overloading that I said in the original reply. "OOP on it's own is very powerful in some places" Yeah, it is. That's why rust has support for many of the OOP things that are good. Inheritance is not one of those. We have alternatives. "the best retained UI frameworks we have seen are all OOP" GUI frameworks and OOP were developed very closely together, yes. Those are kind of a special case, writing GUI apps is very different from almost all other fields in programming. Rust wont add a feature that makes it slightly more appealing for one field while making it worse for a hundred others. "doesn't seem like anyone in the Rust community has found a better way to do so" There are good GUI libraries in rust. They are just not fully mature yet. See druid and iced. "Also supporting OOP in a similar way C++ does would allow for better interop. The current interop with C++ is very lackluster." Neither C++ or rust has a stable ABI, we just use the C abi. As long as they dont have stable ABIs, inheritance OOP support for the sake of compatability is simply irrelevant. Compatability with C++ is not a priority of rust anyway. For that, see carbon. Also the languages are designed very differently in many other ways, inheritance support is really not the biggest problem. About the windows crate, I know extremely little about that, I dont have anything I could say about it, sorry. "They are supposed to make life easier, not harder." Rust makes things harder in the initial stages of development, but it's not meant for that. The best usecase for rust is real codebases that have a lot of maintanance, and inheritance in bigger codebases very often leads to software design that is very difficult to refactor. Rust cares more about the long term consequences.
function overloading would just add new ways to break Rust's type inference and type system. It's even worse for generic functions, where it can straight up introduce soundness holes (have a look why specialization is not stable yet). And rust is extremely generics-heavy language (keep in mind, that lifetimes are generic parameters too). That's a tall price to pay for a feature, that's effectively just syntactic sugar to let you use the same name for different functions. As for class inheritance, most of what it does is already covered by traits, enums and generics in superior way. The cases where class inheritance actually adds anything of substance to the table are few and far between. Classes and inheritance were left out for good reasons.
@@NoBoilerplate Sorry if the feedback was harsh, I will give it a watch. I see what you're getting at and it's not the easiest to summarize in a few words!
ERRATA
- While Clippy's nursery group is very useful for running locally, it's got lints that aren't ready for production use; they can have false positives or plain wrong suggestions. Don't use them with --fix.
comment.content.expect("something")
@@rpitit not anymore!
Your syntax highlighting seems to be wrong in some of the code blocks.
@@SkyyySi It likely is a bit wonky, I have to do a lot of hacking around, and I have no customisation of reveal.js as used in Obsidian.
Could you give me a time code for an example?
@@NoBoilerplate 1:00 is an example of highlighting issue
I think it was a bit too short for such huge subject, but I will have a few things to search for. 🙂
PS: The only channel where I don't even skip the "sponsor" section.
Thank you!
yeah, I couldn't fit it all in! Hence the plan for a part 2 in the future :-)
@@NoBoilerplate hopefully testing in Rust can be a miniseries of its own. I work in automated testing, and suffice to say I'm heavily invested in the topic 😁
@@penguindrummaster good idea!
Much better this way 🤜🤛 thanks for the videos!
@@NoBoilerplate i think you could plan a new video for each tool in this video, subject is so huge and so much important !
So glad I found your channel. The accurate, no-nonsense, logically argued information with some interspersed humour and little reminders of sanity checks has really helped. Thank you.
Every single one of your videos brings a smile and some new knowledge. I love how concise they are!
Thank you so much! I am trying to branch out into other technical topics, so stay tuned!
@@NoBoilerplate Can you give us a hint as to what other topics you are thinking of branching?
@@Sashin9000 Ask me on Discord ;-)
Great video, as always! I'd say it's appropriate to compare features of Rust only with those found in other compiled languages with strict static typing (Go, Java, C++). I think dynamic languages serve such a fundamentally different purpose, that they, as tools, are not even neighboring Rust. They are completely different, with their own use cases and advantages, the main one being interactivity.
Thank you for your comment. As a python developer of 15 years, I agree with much that you have said. Even with my demonstrable excitement with Rust, I shy away from recommending it in data science, where interactivity is vital. Though I tell people to try Julia rather than Python, these days.
That being said, a core thesis of my Rust series is that Rust CAN compete in projects that were traditionally dominated by dynamic languages (ruby on rails, python's django etc). This is because what interactivity you lose through not having a runtime shell, you gain through rich compiler feedback. And compiler feedback is FASTER.
Further, repl driven development is optional, in Rust, compiler driven development is MANDATORY.
More of my thoughts in this video ua-cam.com/video/4dvf6kM70qM/v-deo.html
@@NoBoilerplate Makes sense! What do you think about Julia's industry adoption and applicability? Does Python's library ecosystem trump its shortcomings as a language of ML and Data Science? And so no other language will see widespread industry adoption in these fields in the near future?
@@user-mt6lk8ld7w I think Python's GIL (which is confirmed NOT replaced in 4.0) is a HUGE problem for parallel computation, python can't stay relevant dragging this ball and chain around.
@@NoBoilerplate Python won't have a 4.0.
Honey, wake up. New NoBoilerplate video just dropped
I'm working on a sqlx-like client for surrealdb, and hearing about the fuzz library was exactly what I needed!
Perhaps my dreams of compile time query testing are within my coding abilities after all
Everything you desire is just a macro away :-D
Yeeees, finaly! This is the only channel I am impatiently waiting for new videos!
There's going to be more videos coming soon, I'm putting in huge effort to the channel this coming year!
I’m moving from Python to Rust, and it’s been quite hard. One cannot simply try to re-write everything with the same logic.
For example, in Rust the Builder Pattern is really great for constructing complex objects, and makes rust far more readable.
In python there’s very little use case for this pattern except for testing purposes.
Keep up the good content! (This channel helps me a lot in my from python to rust journey).
Well done, as someone who also is transitioning from Python to Rust, my advice is: Keep going!
I make my rust videos to show that there's INCREDIBLE features that you simply can't get elsewhere. Have you seen my other videos?
Good luck!
@@NoBoilerplate Yeah, I’ve been following your channel! And yes, Rust has really nice features such as the borrow checker which is a miracle.
It’s far more complicated to write bugs in rust than python.
@@analisamelojete1966 oh lovely, well thank you! 🙂
10:52 just saying about the fire alarm pressing the button only tests that it can make noise while you should actually make a little fire (that has smoke, like burning some paper, not just using a lighter) to test the entire system
Ah, an integration test! This is actually what companies do, don't they? They've got these portable smoke machines on poles that cover individual sensors?
The quote is from the Pact website, fyi
@@NoBoilerplate :3c
I would love to see a full video going into sqlx. Looks amazing
Yes, it's in my backlog, I LOVE sqlx. Looks like they've got migrations now, which is fantastic.
The python example isn't fair in my opinion. You can use type annotations in python and every halfway recent lsp will shout at you when passing wrong types and I think they are easier to read than the rust ones.
As a professional python developer, I too am baffled why Hypothosis doesn't use them!
This is the problem with optional typing: People can ignore them, as much of the python ecosystem does :-(
I do love that it's possible, though! I write interfaces for all my code in mypy. I don't know how I'd stay sane if I didn't!
Python doesn’t enforce the types, though, and many developers still don’t use them (including some library developers). You can’t prove that a variable will be the type you think it is going to be in Python, even with type annotations.
My main problem with Python is that the type system is immature. There aren’t nearly enough features to allow me to express the nuances of a dynamic language, so I often fall back on Any and cast, which create errors that are allowed to ripple throughout my codebase. TypeScript solves this by giving you an extensive feature set capable of describing almost anything you might want, eliminating the need for any and casting, but Rust solves it with a deep understanding of the code and all possible states, forcing you to use explicit runtime verification for things it can’t understand like boxed traits (like Any)
Python: sees integer
*extern to object*
@@Zzznmop huh?
The only channel I've got notifications turned on for. Keep it up! Are you planning to do any Dao videos, or that was a one-time thing?
The 'meditation for coders'? Oh I'm absolutely returning to those kinds of videos. I am going to start phasing them in slowly over the course of the next few months. Next video isn't about Rust again, it's about AI!
I'm going to make more videos this year, would you subscribe to a patreon if there were more such videos? I'm trying to work out how best to set up my channel...!
@@NoBoilerplate Well, I'm sure excited to see you cover more things with the same approach you have right now.
Things like comparing Rust to other languages, or providing ways to do things in rust that are typically done in other languages would be good, too (like the Yew video).
I believe making different thumbnails would help the channel, too.
Above all, keep the technical, fast style we all love.
I'm no programmer, but this quality of delivery is very impressive. I think your videos and community engagement is something other project could learn from. "No Boilerplate" and "N-O-D-E" really are talented people.
N-O-D-E is a huge inspiration for me, as you can very likely tell, I'm honored to be mentioned in the same sentence.
I have started producing technical, but non-programming videos this year, stay tuned!
(An old example is ua-cam.com/video/4rWwBYjoiJ0/v-deo.html)
@@NoBoilerplate keep up the good work, one of these days I might start uploading. Really loving the aesthetic.
@@simian3455 Thank you! When you do, try Obsidian.md for your slides, it's incredible!
This is the right type of rust evangelist that I needed in my life.
Welcome to the Cargo Cult! Would you like another video? ua-cam.com/video/oY0XwMOSzq4/v-deo.html&lc=UgzR-SMMc0I39okC1jJ4AaABAg
This was a great video! Very, very nice indeed.
Thank you so much!
Great overview! Thanks for this video. :)
Glad it was helpful!
Great video - coming from python, I found this very helpful. I hope you cover the sponsor's architecture more in the future since it seemed very interesting with the combined microservices, Python and Rust.
No Boilerplate really is a fitting name for this channel!
Thank you! I thought about calling it "no red tape" but that has small-government connotations that I don't agree with!
I forgot how awesome this channel is.
Not been suggested in a while :(
Isn't the algorithm annoying! I have to manually check my subs every day ua-cam.com/users/feedsubscriptions
This is a great video, super concise, so many testing crate suggestions. I learned a ton!
Glad it was helpful!
Thanks for another great video! There are some good crates for mocking complex objects, like *mockall* for struct and trait. Something else I want to try, to complete tests on libraries: *static_assertions* (it's apparently not updated anymore, hopefully because it's not necessary) or *more-asserts* . The one thing that bugs me with "Cargo test" is the lack of option to test combinations of features, and *cargo-all-features* does it.
I'll check it out!
Always love these videos :)
Thank you so much for saying so, I really like making them! :-)
havent coded in rust yet, but your videos are still very entertaining and informative. thank you for your great work!
That's so kind of you to say! Do come say hi on my discord in #newbie-advice if you want help getting started, lots of nice people there :-)
So you could take a C library, wrap it in Rust's type system based on the documentation, and then use fuzz to automatically fuzz the C library with all supposedly valid inputs based on the type information your provided via Rust.
You could, though there are many mature fuzzing libraries for C, too!
OMG, perceived this as your densest video so far.
Got to do some active coding work and watch it once (just once?) more after... 🤩
Thank you! Interestingly it's my least popular so far - testing's not the most exciting topic I guess XD
@@NoBoilerplate Well, took a liking to it when Erich Gamma said: "test a little, code a little, test a little, code a little." 😇
@@TobiasFrei hehe, TDD I really dig
Golang tests are magic, project of any size will compile within few seconds and test run takes few seconds, while nodejs rust etc take time to compline decent size project and then run those tests as well
I really like Go! No arguments that it's got fast tests, but I'm not so sure they're magic other than that.
Back in 2020 I evaluated three languages (go, haskell, and Rust) to see which I would focus on, I know it could be any of the three. I went with Rust because of the astonishing number of features packed in that other popular languages just don't have.
Rust's compile times are slower than Go's because they're doing HUGE amounts of work for you that Go can't do due to its simpler type system. You might not know this if you're new to rust.
Two of them are Rust's Macros and the Unsafe system, I did a deep-dive on both here, and I'd love your take, if you have time: ua-cam.com/video/PuMXWc0xrK0/v-deo.html
I'm in a tough spot with rust. I can't really think of any technical issues with it, but I really just don't enjoy writing rust code.
I feel like I'm in a constant battle with the compiler and it never gives me anything back for my hard work. I get guaranteed memory safety but 99% of the issues with programs I write aren't memory issues.
I think saying it is the future is probably valid and more power to the people who want that, and I probably should too, but I'm not sure it's a future I want to be a part of as it just feels less enjoyable.
Rust is extremely different isn't it! No inheritance, new syntax for lifetime annotations and borrows, and the compiler hates a lot of the standard normal patterns we've been using for decades. I know how you feel.
Back in 2020 I crashed out of learning Rust twice. First time due to multiple string types (Haskell's biggest mistake made again, I thought to myself) and second due to lifetimes. At the time I had a great mentor who picked me up and helped me back on the path. Most people don't have this. This is why I made my Rust series.
Yes Rust is very different from what you're used to. But that's the point. The difficulty curve is because you are learning things no other language can do. Like a bicycle vs a motorbike. Did you see my video on this topic? ua-cam.com/video/4YU_r70yGjQ/v-deo.html
@@NoBoilerplate I have yeah (I think I've seen almost all of your videos).
I imagine most of what I have an issue with is an issue of time and experience. I'm primarily used to garbage collected languages like C# and Java, and memory unsafe languages like C++. Both of them debatably give you a little bit too much freedom is how you solve a problem, and that can result in bad outcomes. However, I feel like Rust leans just a little bit too far the other way for me, as there's been a couple of instances where I know my code is safe in context but the compiler won't allow it because it's not safe in _all_ contexts.
Potentially I should be aiming for all contexts being safe as that's safer for any future changes, but it does feel like this constant extra overhead as I'm writing it. Again though, maybe that's an issue of experience and I just need to write more rust code 😆
@@moff181 It's an overhead at the beginning, absolutely no doubt there. But as you build more and more complex apps, it saves you more and more.
I like to say that in other languages, easy things are easy and hard things are possible, whereas in Rust, easy things are possible, and hard things are easy!
Do you have any planning on Rust tutorials, your videos are too good. Thank you
I have something in the works, but I highly recommend fasterthanli.me, Code To The Moon and The Primeagen for now! :-)
Regarding unwrap, in case you haven’t already, make sure to read the blog post “Using unwrap() in Rust is Okay” by “burntsushi”.
Very good read, thank you (it's here for others blog.burntsushi.net/unwrap/)
I totally agree with the burntsushi's point that "I think it is a good idea to generally recommend the use of expect() over unwrap()."
Where we differ is I don't agree with what they go on to say: "I do not think it’s a good idea to ban unwrap() completely."
Their argument against .expect() is 'line noise', something that personally I'm uninterested in optimising. I love long variable names and verbose documentation!
Thank you very much Frank for linking me to this, it's improved my understanding of panicking in rust :-)
@@NoBoilerplate Of course, different views on this - mostly stylistic - question are possible; as long as one isn’t advocating to never use unwrap OR expect either (or any panic! either, perhaps), it IS mostly a question of style I suppose. AFAIK, there’s also an interesting historical context here, in that unwrap used to be way worse in older Rust versions in that you couldn’t determine the location of the unwrap that panicked from the panic message. Nowadays the panic message DOES include a source location, but before it did (I’m not totally sure when that changed, would need to look into this more to figure it out) there was a very valid point in avoiding unwrap and using expect instead merely to make the error messages objectively more useful by becoming less ambiguous.
In my personal opinion, the more relevant aspect of using either of unwrap or expect is that there’s a comment explaining why the operation is expected to never fail, in all but the most straightforward case. Whether or not some aspect of this reasoning is copied into an "expect" error message string, I wouldn't particularly care. I also noticed that burntsushi wrote in their blog post that they don't use unwrap for avoiding error handling in prototyping. If one never even temporarily incorrectly uses unwrap anyways, the situation is different from when one choses to use unwrap as a linted-against tool in temporary WIP situations. If there are no incorrect unwraps in the first place, you don't need the lint, and if you don't need the lint, you are free to use unwrap "correctly" (in cases where the alternative approach/convention would only have accepted "expect).
I've been waiting for this
Thank you! I hope you don't have to wait too long for part 2 :-D
This is next level good.
Love all videos, but this one is upped a nudge.
Thank you! I'm so glad to hear it, I was concerned that testing, even in Rust is a bit of a dry topic!
Can one also do formal verification with Rust?
I do that with Ada/SPARK and it's great because there can be absolutely no runtime errors since you essentially prove your program.
As systems get larger, edge cases get increasingly harder to detect due to the increase in variables. Testing can only cover so much cases and if you have more variables, there are exponentially more cases.
You can tell by the wording in my video that I'm familiar with formal methods, I hope! "state space" is indeed the problem.
In short, Compromises are needed, I think Rust has the best compromises between formal verification and pragmatism. Let me tell you my story.
At university we were taught Z, B, and Coq. I was devastated that there are no practical verified languages, even using these methods you must translate your model into (usually) C++ or Java.
My top-voted SO question gives you an insight into my mental state at this time stackoverflow.com/questions/4077970/can-haskell-functions-be-proved-model-checked-verified-with-correctness-properti
So I went on to learn Haskell, as it seemed to provide much of the confidence in my code that I wanted, while being a very robust, well-supported language.
However, the esoteric syntax and purity system, while being exactly what I wanted, has kept it back in popularity, which meant I never was able to persuade any team to use it.
Fast forward to 2020, and I was STILL sick of writing code I can't prove. Python with mypy and JS with TS isn't cutting it. I evaluate Go, Rust, Haskell, and Typed Clojure as the top-popularity languages that might have enough safety for me. After crashing out twice, Rust finally stuck.
---
Can you do formal verification with Rust? - Yes. Does anyone yet do it? No, though dependent types are supported by a few crates.
One of the reasons Rust excites me is that it will not take long for someone to embed Coq (or Z or similar) INSIDE a Rust macro, which generates verified Rust code AT COMPILE TIME, and transparently inserts the verified Rust code into the compiler.
I imagine this could best be done at a critical function level, as we do with formal methods.
I will wait patiently for such a system to be written.
---
Even before someone (god I hope it doesn't have to be me!) writes this system, Rust already gives me Haskell-levels of confidence in my code, as I explained at the start of this video. I wouldn't yet build a pacemaker or an autopilot system using Rust, but it doesn't have to do that for me to be excited:
Bringing industry-leading correctness to non-critical applications (like webapps and services) makes me VERY excited, safe in the knowledge that more safety is possible.
I started writing my Rust series because of this excitement, a video of mine I'd recommend to you is this one, about Rust's error safety, especially the compiler-verification that no panics are possible towards the end ua-cam.com/video/sbVxq7nNtgo/v-deo.html
:-)
Lol the python example -- def hello (name: str) -> str:
Proptest & assert_debug
Do not cite the deep magic to me, friend, I was there when it was written ;-)
As a python developer of 15 years, I love typehints, and write all my code using mypy.
The problem in this example is that the Hypothosis used decorators to do typing instead of typehints. I too am baffled why they did that, though they would ALSO have to decorate the function, so maybe they thought they'd do both at the same time?
The problem, and strength, of Python is legacy. Yes you can use mypy and typehints, but most people don't. Typescript got it right - fork the language and make it the default. Though it's still optional in TS, and built on very shaky JS ground...
This video is very humbling.
That's an interesting expression, why do you say so?
I would love to know what you're using for these lovely terminal icons!
Which do you mean? My slides are built with obsidian.md, using the vs2015 theme.
Tell me a timecode!
@@NoBoilerplate 3:32. I figured it must be exa, which sent me into a rabbithole of customizing all the colors 🥲
I wish I could upvote more than once
Same :-)
While the power of Rust is simply amazing, modern Python can ensure type checks with the right annotations.
The only difference is in the mandatory nature of Rust typing, Python's is opt-in.
As a Python developer of 15 years, I see two problems with your statement:
1. Opt-in means opt-out for most. Typeshed has perhaps 100 libraries with mypy stubs github.com/python/typeshed/tree/main/stubs. One. Hundred. Is YOUR TEAM going to write all the stubs for everything you use?
2. Mypy and other type checkers are very limited in their scope. Rust's type checking isn't like C or Java's, or like Python's. The amount of meaning you can encode in it is nothing short of astonishing! I'd love to know your thoughts on my video about it: ua-cam.com/video/4YU_r70yGjQ/v-deo.html
@NoBoilerplate I'll admit that I don't have as much experience, and your remarks are absolutely valid.
In my use case, I avoid external libraries so the lack of annotations didn't really bother me.
However, I have nothing to say about the strength of Rust's algebraic type system. It is indeed much much powerful and expressive.
Really the point of my comment was to poke at the python examples, which can - but mostly won't, for the reasons you pointed - be much safer, easier to document at almost no additional cost :)
@@willexco2001 Surely you need external libraries to use type checking in python, though? Type hints are documentation?
@@NoBoilerplate Sure, that's one exception. Type hints are part of the dev documentation, it's easier to reference the intellisense where the types are formatted in a standard way that integrates in the editor. I'm well aware that it's not close to a full doc, but it helps a lot, compared to text in a docstring.
@@willexco2001 oh absolutely, I love that Python standardised them with the `typing` module, too - a constant relief when coding in python!
Man, Rust is really commited to getting all of the value out of it's type system.
"Machine-readable JSON" (10:33) It's a text file, 😆
The tools showcased here seem to be pretty good advancements in web-dev and CRUD apps.
Did you see my video showcasing Yew.rs? The type system is such an incredible thing! I've known about algebraic types for over a decade by this point (I learned haskell in 2009) but I never thought I'd see it in a popular, practical language!
My yew video: ua-cam.com/video/P4LMfkFLRsI/v-deo.html
11:04 Then why aren't there parametrized tests?
Exhaustive when (== match in Rust) blocks are also available in kotlin, doesn't really seem like a specific rust feature to me.
Nearly nothing rust does is new.
Novelty isn't what I'm excited by.
Rust's compromises of what to put in and what to leave out are SO GREAT! Rust's not exciting, it's boring, as I said here: ua-cam.com/video/oY0XwMOSzq4/v-deo.html
An important quote from the video: "If no new answers are needed, then don't write an if statement. Add more detail to your model."
Thank you! Yes that's a huge realisation for me. But: Sorry I wasn't clear, the line is "If more nuance is needed, then don't write an if statement, add more detail to your model." You can always refer to my compile-checked markdown to find the script!
github.com/0atman/noboilerplate/blob/main/scripts/16-rust-testing.md?plain=1#L155
@@NoBoilerplate ahh I see it makes a better sense now :)
@@udianilbey I love how much business logic can be encoded in the type system 😀
@@NoBoilerplate me too! :D Learning Rust makes me realise it and then it affects how I write code in other languages as well.
@@udianilbey same! I used to think this about haskell too!
Yeah, one thing I didn't like about learning C is how often my program would compile and then proceed to not work
Right! That's just rude. My dream is to encode so much of my logic in the type system that the compiler keeps my business logic safe from my own stupidity!
Thanks for such an informative video. Can you please do a video on Rust build integration with Azure, AWS, Google cloud? thanks again.
Noted, might take me a while to get there. I can help now, however!
Of those three, AWS is 1st-class aws.amazon.com/sdk-for-rust/
Azure is good github.com/Azure/azure-sdk-for-rust
GCP lacks first-party support, though many crates are available crates.io/search?q=gcp
Rust doctests seems kind of gross. Equal amounts of setup code, no static analysis of the test, no function usage help from IDE, if there are any changes in helper functions or std then no static warnings. Is there any open source codebases which use doctest where it makes sense, and is this a pattern worth preserving from other programming languages?
Huh this is EXTREMELY bad news. I didn't realise doctests weren't compile checked in the same way as regular code, I can indeed confirm that even in vscode, with the rust packages installed, though it's syntax highlighting it, it doesn't interact with lsp.
Very disappointing github.com/rust-lang/cargo/issues/6424
Perhaps it would be best to use them just for very simple uses, like an acceptance test, that rarely changes, then it can run on CI
@@NoBoilerplate Doc tests don't really interact with the LSP or cargo check, but they are run as real tests when you run cargo test.
Also when I write a doc test in vscode (I'm actually using vscodium), it brings up a button I can click "Run Doctest".
Once when I was browsing docs dot rs, the place where every single rust library is automatically documented, I saw code blocks with warnings that the code examples were not run.
Whenever you're reading documentation on docs dot rs and see a code block, you can know that the code is guaranteed to work in the way that you would expect from what the code is.
If that code block was not run as a test on that release of the crate, it will warn you about it and you can hover over the information icon on the left of the code block to see it explain that it was never confirmed to run correctly.
If you want to see an example of this information icon, you can see it in the docs of Option::unwrap where the test is deliberately supposed to panic when calling it on None.
There the warning that it deliberately panics is red, and if the doc test was not run, it would be orange if I recall correctly.
I assume there would be an even clearer warning if the test was run but the result was not as expected.
Hi Tris, how would you feel about looking into getting your vids onto Nebula (I have no idea how that works or how you'd do it) it would be cool to be able to watch them away from UA-cam which I feel is fighting for my attention. Like there's all these little things meant to make me stay on the platform, click on ads etc.
I'm with you on that one, and you're not the first person to ask me to host off-youtube. My solution is to host ad-free, tracking-free videos on my Patreon, for pay what you like: www.patreon.com/noboilerplate
I'm slowly building up the community around patreon, and currently there's behind-the-scenes posts and a vip section on my discord. I'll be adding more features this year, stay tuned for that!
I'm excited that No Boilerplate is becoming more than a hobby for me, but for it to be sustainable, I have to think about how to pay myself for my time. My hope is to grow to weekly videos over the course of the upcoming year, but to do that I'll have to work part time at my day job. Exciting, but also terrifying!
You could also try using RSS. I think there is a way to addd UA-cam channels' videos to your RSS feed, which bypasses the UA-cam website altogether and just gives you a video whenever the channel uploads. I think there's also a way to hook this up to yt-dlp so that you don't have to watch the ads either ^^.
@@spaghettiking653 Ooh, I didn't know there were RSS feeds for channels, that's GREAT! Looks like it's pretty simple once you know: stackoverflow.com/questions/64232403/rss-feed-for-youtube-channels
would protobuf make the need for pact contract testing obsolte?
Just because you know the schema, doesn't mean both sides will use it! It would make contract testing MUCH easier, certainly. I'd still want to run it, you can't trust external services ;-)
just checked the fuzzing option, and first line of text is, that it does not work yet :( (on x86 Windows)
> Note: libFuzzer needs LLVM sanitizer support, so this only works on x86-64 Linux, x86-64 macOS and Apple-Silicon (aarch64) macOS
If you're coding on windows, as far as I understand, you MUST use WSL. This goes for all open source programming languages - the Linux ecosystem is ubiquitous, and microsoft have done an AMAZING job with wsl2 - try it out!
@@NoBoilerplate using WSL to run WINE sounds like pretty crazy idea :( If that is even possible to set up, I do not think it would be really easy.
"Rewrite in Rust" It feels like there is so much I do not understand, so that is not possible with my current skills :(
If you would be able to do that (especially if you would be able to offer explanations of steps taken, so I can learn from it) I would be willing to negotiate some financial compensation.
The function I am interested in right now generates a random map for a game. Theoretically inputs are `seed: NonZeroU16`, `pve_pvp: bool`, and player count (pve: 1, 2, 4; pvp: 2, 4, 6), and game configuration files.
Requirement would be that the rust version must generate exactly same map with same inputs (and must not be slower).
If you want to reach me for details my nickname on Discord is `kubik`, or you can use email `rewrite_in_runst@simple.login.bandola.cz` (I am using simplelogin.io/ when putting an email address on place like this).
@@NoBoilerplate I guess getting one answer was more than lucky enough for me 😢
What's your opinion of Clojure?
ADORE clojure, I wrote it professionally at a bank for 2 years a while back.
My very first NB video was on Clojure! ua-cam.com/video/k_4rLyqQeAA/v-deo.html
However, it's hampered by the JVM so much. I now get the same joy in programming I used to get in lisp with Rust. Instead of a conversation with a runtime, evaluating sexps, I have a conversation with the compiler. It's fantastic! Here's my video on this effect ua-cam.com/video/CJtvnepMVAU/v-deo.html
The main thing I found disappointing about rust tests is they can only produce "pass" and "fail" results. I'd really like to see "skip" (this test can't be run because of some hardware or environemnt issue) and "xfail" (this test failure represents a known bug)
That would be handy! But that's just a test framework feature, not a language feature, right? I imagine one of the test frameworks has the features you would like.
As for hardware, I believe you would do this sort of thing by using the cfg attribute, such as here, when you don't want to run a test on wasm:
#![cfg(not(target_arch="wasm32"))]
Docs: doc.rust-lang.org/stable/rust-by-example/attribute/cfg.html
@NBP - Soooo happy I found (and subbed to) ur channel. #mindenlargement
Welcome! 😀
I wonder if PHP has taken some inspiration from Rust in the last years. Function return types and match constructs look very similar.
function foo(): String {}
$a = match($b) {
1 => doOneThing(),
2 => doTheOtherThing(),
default => doTheDefault(),
};
I'm in the process of learning Rust and it makes me respect PHP a little more seeing how it has some of the same features that many other scripting languages don't. I still want to leave PHP because PHP devs are dragging the language down, many of them still see types etc as "visual clutter" or "making the project harder to change" and I just don't want to have these kinds of arguments in my team anymore.
ugh, that's a nice thing about rust is that everyone's on-board with sensible correct code!
Something tells me this guy likes rust
What gave it away XD ua-cam.com/video/Q3AhzHq8ogs/v-deo.html
Maybe I'll actually start writing tests now. LOL just kidding. I don't learn my lesson until I've got something to cry about, it seems. Looks way better than the js testing I never did, though
You're gonna love Rust - get safer code than most other projects that USE tests - without writing a single test!
I love your work here, but I think you're going a bit far comparing an loosely typed language like Python and a strongly typed language like Rust. Can you do comparisons against another strongly typed language, like say C#?
I'm afraid I don't know C#, I'm a python developer by trade, so that's what I compare against mostly (and JS too).
Most popular languages are dynamically typed, however:
1 JavaScript
2 Python
3 Java - Statically Typed
4 PHP
5 C# - Statically Typed
(redmonk.com)
The real difference between Rust and all these other languages for testing is the macro system, in this case. My understanding is that C# doesn't have a lisp-style macro system?
@@NoBoilerplate I would argue that TypeScript also gives you the "superpowers" you refer to. I'm not a Python dev, but in my experience it's a similar issue with any loosely/dynamically typed language. Maybe since JS it at the top of your list, type guarding could be compared using TS. null (and undefined) are a real pain, but TS solves that with strict-null-checks. C# attempts to solve it with "Nullable Reference Types". I think the things that set Rust apart are it's capability to target so many things and do it well.
Thanks for your review of the testing in Rust. But again, testing a statically typed language is much easier for obvious reasons.
@@urbanelemental3308 Rust's solution is to have no nulls - the best solution!
I must insist that Rust isn't just another statically typed language, I'm writing my Rust series to explain this, I'd love to know your thoughts on this video ua-cam.com/video/PuMXWc0xrK0/v-deo.html
@@NoBoilerplate Oh I absolutely agree about not having null as the answer! I'm just saying that there has been interesting effort to help solve/reduce the problem in statically typed languages. And no question, I love your videos. You're doing an outstanding job. Yeah, macros are special! I love them. And if I understand Rust correctly, 'traits' are kind of like extensions in C#. Extensions in C# are what facilitated the LINQ library. Ultimately, Rust started with the lessons of the past and did things right.
All that said. Rust = hard-mode. And I struggle with it. IMO, I think C#/.NET has made some really interesting strides that try to solve a lot of the issues with memory, performance (making unsafe code safe).
@@NoBoilerplate Hey, I'm looking for a crate that has functions for prompting the user for a response and knows how to validate it. Any help here?
🦀
ferris!
I was hoping you'd talk about ChatGPT. I hope you speak about it soon.
Next video is exactly this! Stay tuned!
@@rpitit yeah, I'm deeply unimpressed by the discourse I've seen. I hope to say a few new words on the matter :-)
What do you think about bacon?
I'm never without it, even if I'm using an editor with LSP. It's part of my toolkit, which I talked about here! ua-cam.com/video/ifaLk5v3W90/v-deo.html
I didn't understand what clippy does ...
It's a really comprehensive code linting tool. Tells you when you are using bad patterns and suggests improvements.
IMO these days the only good thing most of your videos is for is for showcasing Rust to devs who *never* tried it. Because if they had written a Rust project, they would know all of these rules and concepts already, otherwise the compiler wont compile.
Your videos are good for the first weeks I began following you (when I first learned Rust and your channel was just born), but then it got boring and every one of them feels the same - just you outlining how Rust is so great, and some small tips etc.
Maybe you should share more in- depth or specialized Rust topics that would be useful for current Rust devs too.
Congratulations on your success! let me be very clear: **My videos are mostly hype by design.**
Most comments on my videos are not by people like you. **They are by total Rust newbies.** Some are even new to programming and are so excited by what they see. If you don't understand Rust, and someone explains it clearly, as I try to, it seems like magic. Rust is the [#19th most popular language](redmonk.com/sogrady/2022/03/28/language-rankings-1-22/), but the scale is not linear. There is a lot of work to do if we are to not suffer the same fate as Haskell.
**I want Rust to achieve escape velocity.**
I see my job not as a details person, I am way under-qualified for that, I point people to fasterthanli.me for details. My focus is funneling new people to Rust, getting them so excited they can't help but push through the learning curve, and then from there language sells itself.
Back in 2020 I crashed out of learning Rust twice. First time due to multiple string types (haskell's biggest mistake made again, I thought to myself) and second due to lifetimes. At the time I had a great mentor (Shout-out to Alex!) who picked me up and helped me back on the path. Most people don't have this. They need the excitement of my hype videos to break through.
I will eventually run out of hype topics and move on to slightly more detail. But first, there's a few million developers I want to send to [rustup.rs](rustup.rs).
:-)
Wait, you're telling me there is no seg fault in Rust?
Everything except cpu errors are pushed to compile time in rust, which is astonishingly good! That covers every case that I care about.
It's possible to segfault inside an unsafe {} block, but you have to TRY really hard to do it :-) stackoverflow.com/questions/62844652/what-is-an-example-of-rust-code-that-causes-a-segfault
code tests test LOGIC, not if the code works or not. obviously it works, it compiled, but that's not the point
My hope with this video, and others in my Rust series, is to show how the rich type systems allows you to encode MORE logic than java, go or c etc, moving more and more of your logic from runtime, which as you've said requires tests, to compile time, which doesn't.
Rust is the real language of programmers
It certainly makes me feel like a genius! Did you see my video on that topic? ua-cam.com/video/0rJ94rbdteE/v-deo.html
would love to know what you think!
Alas, it compiles, but when I run, I'm getting: thread 'main' panicked at 'already mutably borrowed: BorrowError'! Rust sucks.
Wow, you're compiling some very advanced code! Those sorts of interior mutability errors happen very rarely when using some of the data types like `std::cell::RefCell` or `std::rc::Rc`.
There is a wonderful video by friend of the channel Ken, at CTTM that explains these advanced types if you are interested: ua-cam.com/video/CTTiaOo4cbY/v-deo.html
How far along are you in your Rust journey?
@@NoBoilerplate If consider learning a language from 1 to five, I am on 3 now.
@@kamertonaudiophileplayer847 I'd be very happy to help you! Try my intro video
ua-cam.com/video/br3GIIQeefY/v-deo.html
or the article it was based on:
fasterthanli.me/articles/a-half-hour-to-learn-rust
Let me know if I can help 🙂
@@NoBoilerplate Thank you, sure it would help . If you know somebody who 's experienced with Rust internal, for example use Rust crates without Cargo, let me know.
@@kamertonaudiophileplayer847 there's loads of nice people with lots of experience on my discord, come ask in #programming or #newbie-advice!
Almost like Elixir
LOVE elixir. However, the reason I'm not up here shouting about it is, sadly, popularity matters redmonk.com/sogrady/2022/10/20/language-rankings-6-22/EDCIT
EDIT: Brian May explains below how type hinting in elixir isn't great
Type hinting in Elixir is currently in a terrible state. dialyzer is slow, errors are incomprehensive and typically blame the function instead of the function caller, and it can miss serious problems. Have discovered basic type errors in existing deployed code that directly contract the type hints, but dialyzer is unable to detect the problem. Result: genserver crashes intermittently because somewhere in the code, possibly as a result of an old refactor, the state is corrupted with value of wrong type. One of the main reasons I am using Rust more and more.
@@penguin_brian Thank you so much for your explanation, I will remember this.
watching all your videos and comment section peoples talking like aliens I am trying to understand what they are all discussing but I can't because I'm a beginner in programming language
We all have to start somewhere :-)
If you are a complete beginner, I recommend trying out github.com/rust-lang/rustlings - which are tiny, simple coding exercises that start out from hello world and slowly introduce the language to you!
The Rust Book is also a great resource that is beginner friendly doc.rust-lang.org/stable/book/
@@NoBoilerplate thanks for sharing
Thaaaaaaaanks🫶
Aw man! RazerSecure sounds awesome, but the positions I'd be interested in are U.K. only. Booooo.
Sorry about that! Here's a tip: Apply anyway and they might keep your details on file for when they're ready to hire outside the UK!
This tip works for any company honestly. Are you actively looking for Rust work? Try applying to every company who sponsors Rust: www.rust-lang.org/sponsors Worth a shot!
Why can't Rust add function overloading or class inheritance like C++. Not only would this make life 100x easier it also would turn Rust into the most powerful language in the world
I disagree - the point of rust is not and has never been to be like c++, the point is to be rust.
Rust purposely avoided class inheritance because it creates the OOP mess of class trees and method overriding, which is especially visible in java. Traits handle the problem of shared functionality already. Adding class inheritance would provide a second way to solve the same problem, which can be way more confusing to work with (e.g. types vs interfaces in js) and reintroduces the hierarchy issues of OOP.
Function overloading ignores rust’s strength of clear, explicit code - combining two functions with different sets of arguments that do different things under the same name only obfuscates the code and removes the guarantee that code is easy to use/interface with. If functions take different arguments, they are different functions - let them have different names. This makes sure when you write or see a function call you know exactly what types it is acting on.
Trying to use/retrofit rust like c++ won’t work because they’re fundamentally different languages - writing good rust and reaping its benefits requires you to think in rust.
function overloading is nice to have I guess, but its not without its consequences.
Solving which function should be ran is far from trivial when we already have generics (which actually cover many of the use cases if function overloading).
It would also completely break the powerful type inference that rust has.
Rust has some things from OOP, but it intentionally avoids inheritance. Inheritance has multiple jobs, but it lacks the expressiveness to show which purpose its being used for. Composition, generics, enums and macros are all features that cover use cases of inheritance, its just split up to do each job better on its own, and make it much clearer what the intent is.
Trust me, these things were very strongly considered when designing rust, and they were decided against.
@@theroboman727 I need to disagree.
First of all, function overloading is required when you have different amounts of arguments for each function. Functions expose behaviour, therefore it would make sense that two functions with the same behaviour should have the same name. With Rust however you are supposed to be even more verbose than you already are.
Secondly, OOP on it's own is very powerful in some places, and sometimes even better than the way Rust does things. For example the best retained UI frameworks we have seen are all OOP. And it doesn't seem like anyone in the Rust community has found a better way to do so. Also supporting OOP in a similar way C++ does would allow for better interop. The current interop with C++ is very lackluster. Missing simple OOP capabilities is also the reason why we will (for now) never get any Xaml capabilities in the windows crate. Why can't we have both? Nobody wants asks for a stupid GC or exceptions but rather just simple inheritance capabilities. Rust would be the perfect language to have that.
People don't understand what goals programming languages have in general. They are supposed to make life easier, not harder. Rust makes it way too hard to write such stuff, even Assembly would do a better job at that point.
@@CXCubeHD
"First of all, function overloading is required when you have different amounts of arguments for each function"
Its not. The first approach you could take is that you can take a slice &[T] as an argument and treat that as the parameter list. Or you could use const generics with an array like this:
fn takes_n_numbers(args: [i32; N]) {
Really the only difference to variadics is that you have to write extra []
The third approach is to use a macro, not a function, for what you want. This is why println! is a macro
"therefore it would make sense that two functions with the same behaviour should have the same name"
They aren't exactly the same though. The fact that you had to code a second overload shows that there's something different going on, even if its minor.
"With Rust however you are supposed to write even more bloat"
what do you mean? the amount of code with function overloading vs without is the exact same, just that with function overloading youre putting all of the functions under the same name.
The only difference when youre using the function is going to be that you write `function_x_with_y(x, y)` instead of `function_x(x, y)`. That's not bloat, you just need to be slightly more explicit about it.
Yes, its a tiny bit more boilerplate that may not be necessary depending on the situation, but that is insignificant compared to the consequences of overloading that I said in the original reply.
"OOP on it's own is very powerful in some places"
Yeah, it is. That's why rust has support for many of the OOP things that are good. Inheritance is not one of those. We have alternatives.
"the best retained UI frameworks we have seen are all OOP"
GUI frameworks and OOP were developed very closely together, yes. Those are kind of a special case, writing GUI apps is very different from almost all other fields in programming. Rust wont add a feature that makes it slightly more appealing for one field while making it worse for a hundred others.
"doesn't seem like anyone in the Rust community has found a better way to do so"
There are good GUI libraries in rust. They are just not fully mature yet. See druid and iced.
"Also supporting OOP in a similar way C++ does would allow for better interop. The current interop with C++ is very lackluster."
Neither C++ or rust has a stable ABI, we just use the C abi. As long as they dont have stable ABIs, inheritance OOP support for the sake of compatability is simply irrelevant. Compatability with C++ is not a priority of rust anyway. For that, see carbon.
Also the languages are designed very differently in many other ways, inheritance support is really not the biggest problem.
About the windows crate, I know extremely little about that, I dont have anything I could say about it, sorry.
"They are supposed to make life easier, not harder."
Rust makes things harder in the initial stages of development, but it's not meant for that. The best usecase for rust is real codebases that have a lot of maintanance, and inheritance in bigger codebases very often leads to software design that is very difficult to refactor. Rust cares more about the long term consequences.
function overloading would just add new ways to break Rust's type inference and type system. It's even worse for generic functions, where it can straight up introduce soundness holes (have a look why specialization is not stable yet). And rust is extremely generics-heavy language (keep in mind, that lifetimes are generic parameters too). That's a tall price to pay for a feature, that's effectively just syntactic sugar to let you use the same name for different functions.
As for class inheritance, most of what it does is already covered by traits, enums and generics in superior way. The cases where class inheritance actually adds anything of substance to the table are few and far between. Classes and inheritance were left out for good reasons.
Because of the clickbaity title, I'm afraid to say I won't watch the video. "Testing not needed" is too misleading.
That's true, I have changed the title to reflect the content, thank you
@@NoBoilerplate Sorry if the feedback was harsh, I will give it a watch. I see what you're getting at and it's not the easiest to summarize in a few words!
@@sb_dunk Oh don't worry, my fault entirely! You are quite right, it was too clickbait-y. I'd love to know your thoughts on the video 🙂