I am trying to “memefully” learn Rust. The endgoal is to be competent enough to create a Tauri based GUI software.
For the “memefully” part the goal is to just enjoy the learning process as much as possible. Rust is a hard language, we all know that. So, I plan to write something in Python, then in Nim and then in Rust. Then write a blog about it. I plan to follow the Rust CLI book [0], however I want to do random projects. This weekend’s plan is to write a CLI that takes makes async get requests to a bunch of input URLs.
async Rust feels a bit more like C or C++, where performant and versatile take top rank, and one has to have a lot of experience to know how to avoid sharp edges.
I recommend considering using ureq instead, especially (but not only) if you still are learning Rust and you see Rust as hard.
The costs of async are worth paying, if you’re writing an HTTP server that must serve many many clients with minimal overhead. However, for HTTP clients, we believe that the cost is usually not worth paying. The low-cost alternative to async I/O is blocking I/O, which has a different price: it requires an OS thread per concurrent request. However, that price is usually not high: most HTTP clients make requests sequentially, or with low concurrency.
For several years I thought that Rust was too hard and didn’t even attempt it seriously. Then last year I started the Advent of Code 2022 in Python (that I use professionally), and found this masterpiece from Amos: https://fasterthanli.me/series/advent-of-code-2022/part-1
It was super refreshing as it was showing me how close to Python basic Rust can be, but also with many additional features that make it amazing.
I was able to do about the first 13 days in Rust thanks to that series (I was solving it before looking at the solution from Amos). I must say that it was an eye opener to me. I really recommend this series.
If you ever have trouble tracking down what element is causing overflow, add this rule in the browser inspector:
* { outline: thin solid red }
This makes the bounding boxes of every single element visible. Very helpful.
The author mentioned using work-break: break-word. I have an open question on that rule: Is there any reason not to set it on the body on most sites? I’ve gone on quests to remove scrolling from user-entered content, and I end up setting word-break in a ton of different places. I’m not sure of the trade-offs of just setting it globally.
The author mentions one pass that breaks the constant-time properties: the “x86-cmov-conversion.” He proposes extending the compiler to allow programmers to selectively disable this pass on certain variables.
Now, all of this counts on a sufficiently smart compiler™, but it seems to me that this would have no performance impact; the compiler pass would only be disabled in the case of variables marked as secret, and these variables were already not using the pass.
Of course if human can write fast constant-time code, then sufficiently smart compiler can too. But in practice it’s very hard. The stricter the semantics, the harder it is to optimize. Compilers split optimizations into passes, because that’s a way to have manageable complexity, but that makes individual passes less smart about global view and higher-level semantics of the code.
Passes are designed to be independent for simplicity, and yet interact, to achieve better results together. For example, common subexpression elimination can leave unused code behind, and rely on dead store elimination to clean this up. But later dead store elimination won’t be aware whether it’s eliminating unused code of folded subexpressions, or removes an attempt to clear secrets from memory. And you could try to add more metadata to track these things across passes, but preserving that metadata throughout transformations makes things harder (e.g. LLVM already struggles with preserving aliasing info, which prevents Rust from optimizing better).
I really wish that instead of data classes, which force me to invent types for my data, we’d get a nice built-in syntax for named tuples that wouldn’t require importing a module and repeating the record name twice.
I really wish that instead of data classes, which force me to invent types for my data, we’d get a nice built-in syntax for named tuples that wouldn’t require importing a module and repeating the record name twice.
Well, you still have to specify the types of your data and do an import, but there is a nice alternative syntax for named tuples if you want to avoid repeating the record name:
class Component(NamedTuple):
part_number: int
weight: float
description: Optional[str] = None
I am trying to “memefully” learn Rust. The endgoal is to be competent enough to create a Tauri based GUI software.
For the “memefully” part the goal is to just enjoy the learning process as much as possible. Rust is a hard language, we all know that. So, I plan to write something in Python, then in Nim and then in Rust. Then write a blog about it. I plan to follow the Rust CLI book [0], however I want to do random projects. This weekend’s plan is to write a CLI that takes makes async get requests to a bunch of input URLs.
[0] https://rust-cli.github.io/book/index.html
Async causes a significant fraction of the difficulty of Rust. Nicholas Matsakis, long-time leader of Rust’s language design team, admitted last year that
I recommend considering using
ureq
instead, especially (but not only) if you still are learning Rust and you see Rust as hard.I concede that this depends on how large your “bunch of input URLs” is.
Thank you very much for the recommendation. Trying it out now. It looks easier than reqwest.
Or reqwest with the blocking feature flag
Which just launches a
current_thread
Tokio runtime, uses it to send a request, then drops it. As far as I know, creating a new Tokio runtime per request isn’t that expensive, but it’s definitely more overhead than ureq.I did not know that. Thanks!
For several years I thought that Rust was too hard and didn’t even attempt it seriously. Then last year I started the Advent of Code 2022 in Python (that I use professionally), and found this masterpiece from Amos: https://fasterthanli.me/series/advent-of-code-2022/part-1
It was super refreshing as it was showing me how close to Python basic Rust can be, but also with many additional features that make it amazing. I was able to do about the first 13 days in Rust thanks to that series (I was solving it before looking at the solution from Amos). I must say that it was an eye opener to me. I really recommend this series.
Fasterthanlime and No Boilerplate is the reason I am trying to learn Rust. Thank you very much. Checking the blog posts out.
Oh hey, I did something very similar to your batch URL requests recently! Here was what I came up with: https://github.com/adamhammes/rust_web_serial/blob/master/src/main.rs#L29-L48
That is nice. Thank you for sharing. Bookmarked. Now, I have to expand my feature list a bit more and throw JSON deserialization in the mix.
Some thoughts on horizontal overflow:
If you ever have trouble tracking down what element is causing overflow, add this rule in the browser inspector:
This makes the bounding boxes of every single element visible. Very helpful.
The author mentioned using
work-break: break-word
. I have an open question on that rule: Is there any reason not to set it on the body on most sites? I’ve gone on quests to remove scrolling from user-entered content, and I end up settingword-break
in a ton of different places. I’m not sure of the trade-offs of just setting it globally.Some googling reveals that
break-word
is not CJK-aware, but I’m no expert myself. I only publish English content, so this is fine for me :)The article hypothesizes:
These two seem at odds with each other. Even the breakage example in the article is caused by an optimization pass.
The author mentions one pass that breaks the constant-time properties: the “x86-cmov-conversion.” He proposes extending the compiler to allow programmers to selectively disable this pass on certain variables.
Now, all of this counts on a sufficiently smart compiler™, but it seems to me that this would have no performance impact; the compiler pass would only be disabled in the case of variables marked as secret, and these variables were already not using the pass.
Of course if human can write fast constant-time code, then sufficiently smart compiler can too. But in practice it’s very hard. The stricter the semantics, the harder it is to optimize. Compilers split optimizations into passes, because that’s a way to have manageable complexity, but that makes individual passes less smart about global view and higher-level semantics of the code.
Passes are designed to be independent for simplicity, and yet interact, to achieve better results together. For example, common subexpression elimination can leave unused code behind, and rely on dead store elimination to clean this up. But later dead store elimination won’t be aware whether it’s eliminating unused code of folded subexpressions, or removes an attempt to clear secrets from memory. And you could try to add more metadata to track these things across passes, but preserving that metadata throughout transformations makes things harder (e.g. LLVM already struggles with preserving aliasing info, which prevents Rust from optimizing better).
I really wish that instead of data classes, which force me to invent types for my data, we’d get a nice built-in syntax for named tuples that wouldn’t require importing a module and repeating the record name twice.
Well, you still have to specify the types of your data and do an import, but there is a nice alternative syntax for named tuples if you want to avoid repeating the record name:
Thanks, I didn’t know about the capitalized
NamedTuple
.I agree, the types seem like an unnecessary complication.