Canned tomatoes + salt 🙃
- Posts
- 5
- Comments
- 1329
- Joined
- 6 yr. ago
- Posts
- 5
- Comments
- 1329
- Joined
- 6 yr. ago
I always thought Wostershersauce was supposed to be BBQ-ish. 😅
Originally, they were made from the marshmallow plant, by the way: https://en.wikipedia.org/wiki/Marshmallow#History
But yeah, the modern-day formula is quite different and rather highly processed...
- ß isnt used when you have a pair of s letters next to each other. Its most commonly used if you have long vowels beforehand. See “Trasse” vs “Straße”.
Perhaps worth adding that we had a spelling reform in 1996, which kind of put this rule in place.If you learned German before then or had a teacher who learned it before then, it's possible that you got taught it the old way...
Oh yeah, I'm not saying this is what makes Rust special. Rust's strength in comparison to Bash is that it's a lot more competent at control flow and structuring programs. But yeah, virtually any programming language is at least better at that than Bash, so whichever one you're most comfortable with, is probably the best choice. This trick just allows you to make use of Bash's biggest strengths, which is easily running commands and piping between commands, while also having the competent control flow and structuring of your programming language of choice.
"I think proc macros are a really big superpower for Rust."
Yeah, been working on a framework and proc-macros are really useful to simplify the API.
For example, previously I needed users to define a struct and then an
implblock with a function inside. Well, and they need to do that a lot, so it was genuinely a bit of a pain to write out, but it also made the code feel more complex than it really was.Now I've got an annotation, which you can slap onto a function and then it generates the struct from the function parameters and puts the function into the
implblock.And while you do need to be aware that the function parameters will be put into a struct and therefore you want owned parameters, all-in-all it still feels really nice to just delete dozens of boilerplate lines and an indentation level in the codebases where I've introduced it.One of the simplest tricks is that you can throw down a function, which you can call with a command like e.g. this:
run("cat /etc/os-release | grep NAME")by constructing a Command like so:rust
Command::new("sh") .arg("-c") .arg(command) //the string passed as parameterThere's proper libraries to make running commands even easier and more robust, but if you don't want to pull in a library, that's really easy to write out ad-hoc and gets you 95% of the way there, with shell piping and everything.
I have a big collection of Creative Commons songs. And I wrote a little web music player to stream from my home-server, which I use for listening when I'm working from home, since I didn't want to install a music player on my work laptop..
I really don't agree with saving keypresses being a useful metric, since auto-completion is a thing and code is read significantly more often than it is written. I am also a staunch opponent of abbreviations being used for variable names.
But I will say that I don't mind abbreviations in keywords, since well, you need to learn the meaning either way.And yeah, I've come to somewhat like them being used for keywords, since it reduces visual noise where it really isn't useful, and it distinguishes keywords from actual code.Ultimately, keywords are just syntax where letters were used instead of a symbol. You do read them like a symbol, so if they don't look like a real word, that seems to work quite well for my brain.
Well, you'd typically put the loops into a function and then do an explicit
returnto jump out of there. I believe, there's some use-cases where this isn't possible, which is why I'm cool with loop labels existing, but I've been coding Rust for seven years and have not needed them once...I'm not sure, what you mean by "Chekhov's footgun", but well, it isn't a footgun, so you won't accidentally return a wrong value from the function, if that's what you're thinking.
It's not a Rust invention, most functional programming languages have implicit returns, but basically think less of them as a function return and more just as the value that a scope evaluates to.
So, here's a simple example:
rust
let sum = { let x = 5 + 9; 3 * x };Obviously, this is an extremely contrived example, but yeah, as you can see, it does not even have to involve a function. The implicit return makes it so that
sumis set to the result from3 * x.And the scope-braces are nice here, because you can do intermediate steps without havingxin scope for the rest of your function.In practice, if you see scope-braces and the line at the end does not have a semicolon, then that's the value that the whole scope evaluates to. Those scope-braces can also be the braces of a function, but then you need to annotate what the function is going to return, too, so it's practically impossible to return a wrong value.
Well, and I would actually argue that explicit returns are a footgun in comparison.Because someone might introduce clean-up code at the end of the function and not realize that an explicit return skips that clean-up code, somewhere further up in the function.The implicit return always has to be at the end of the scope, so it's not possible to accidentally skip code.
That is, like, genuinely an advantage, though. At $ DAYJOB, we have a project that spans embedded, backend, web frontend and CLI, and for all of these, Rust is decent.
Like, I can see why a frontend dev would want to use HTML+CSS+JS/TS (rather than HTML+CSS+Rust), mainly because the massive ecosystem of JS components makes you more productive.
But you pretty much won't ever develop a web frontend without an accompanying backend, and then being able to use the same language-expertise, libraries, utility functions and model types, that is also a big boost to productivity, especially if you won't have a dedicated frontend dev anyways.
Realizing that also made me understand why people subject themselves to NodeJS for their backend, which has the same advantage, just with the big ecosystem in the frontend and the small ecosystem in the backend.
Very interesting, thanks! 🙂
Huh, did that emerge out of unrelated design decisions or did they just figure
I know some of my programs used to have lines with just x.unwrap().unwrap().unwrap() or whatever, which is not pretty.
That goes away with experience, though. At least, I can't think of a reason why you'd nest three Results or Options. Normally, you would collate them right away.
The most you see in the wild is something like
Result<Option<_>>to express that a check can fail, but even if it doesn't, then a valid result can still be that there is nothing there.If you don't care that your program crashes (like
.unwrap()does), thenanyhowis the error handling library of choice. With it, you can just write a?in place of an.unwrap()for practically any error type. And well, it automatically combines the errors, so you won't be writing???either.To be honest, I think, they both have their place. In Rust, you typically wouldn't return just a
bool, but rather the element that you removed, so like this:rust
fn getofmylawn(lawn: Lawn) -> Option<Teenager> { lawn.remove() }And then with such a more complex return-type, C-style means that you can't see the function name right away:
c
Option<Teenager> getofmylawn(Lawn lawn) { return lawn.remove(); }I also really don't think, it's a big deal to move your eyes to the
->...Yeah, I was gonna say, error handling easily makes up 80+% of the code paths, and depending on whether you're building a library or service or script etc., different strategies are most suitable for how to deal with those code paths.
In a script, you often just want it to crash. In a library, you want to make these code paths matchable, in case the user cares why something failed. And then you have the awkward in-between, which is that 99% of your application codebase will be used by your main-function like a library, but you don't want to spend as much effort on error handling for that as a proper library does, in particular also because you know what all consumers of your application-library need to know.
So, it's kind of multiple different problems, with overlap, and people are hoping for one easy solution to cover all these problems.
Yeah, like YAML, which's name originally meant "Yet Another Markup Language*.
Then they redubbed it to mean "YAML Ain't Markup Language", because well, it isn't actually a markup language. 🫠
Yeah, as I'm implementing this, I'm also now noticing how problematic that crate split is.
If you want to generate code which uses (non-generated) types that your crate provides in its public API, you have to re-export the macro from a separate crate that also re-exports the types.That in itself is clunky, but it's just complexity for the implementer, not the user, so it's fine.
But where it stops being fine is that this means you cannot easily provide a usage example in the macro's documentation.If you try to use it like normal, it has to generate code referring to your API crate, which will not be defined in your macro crate, so the documentation test won't compile.
So, basically your choices are:
And yeah... if you've coded Rust for long enough, you will recognize these patterns of bad/missing usage examples, which is particularly problematic, because proc-macros are rarely terribly obvious to use. Really hope they can fix this sooner rather than later.As far as I can tell, it would even be a valid fix, if editors and such consistently showed the documentation declared on top of the re-export, then you could write out your usage example there.