Sounds like a lot of my notes could be applied to The Book, heh. (I haven't read it completely in some time.)
I agree with that, and have made my share of similar abstractions -- that don't expect a fake command for the literal or read-from-file versions. I guess it's a presentation problem more than anything (I think a typical program arrives at the generic version after starting with the args-only version, not after starting with some method that can only be held one way). Maybe it could be...
- Take a
Vec<String>
(that doesn't include the command name)// usage let args = env::args().skip(1).collect(); // Or your own custom args! let args = vec!["foo".into(), "bar".into()]; build(args); // Or builder.build(args) or whatever I didn't relook it up
- "Yeah inefficient and clunky, but don't worry. We'll improve it later"
fn build<I: IntoIterator<Item = String>>(iter: I)
build(env::args().skip(1)); build(["foo".into(), "bar".into]);
fn build<S: Into<String>, I: IntoIterator<Item = S>>(iter: I)
build(env::args().skip(1)); build(["foo", "bar"]);
Though this still loses out on handling non-unicode gracefully.
You could use those without unsafe
, but it would make String
not Sync
. You could "work around" that with new types and unsafe
, but it would then be unsound again -- they are !Sync
for a reason after all (and it's to protect you from data races, not to make your life harder arbitrarily). There is a safe and sound way to do it: atomics.
I think the higher level point is that when we're talking about Rust's memory safety model -- so things like aliasing and data races -- it is impossible for the target audience to "know better" or "be smarter than the compiler"; it's almost guaranteed that if they attempt unsafe
for these uses, they will get it incorrect. So one shouldn't imply that it's possible for them to know better than the compiler without putting in the work to understand what Rust considers UB.
Especially if they're coming from C/C++, they might think they know better already. I certainly did, when I started with Rust. But (a) Rust's invariants are different from Cs, and (b) as it turns out the C ecosystem is a lot more tolerant of things like "the language says it's UB but the compiler let's you get away with it". See also this recent thread. It might be nice to point this out to the audience.
It'd be even nicer to give actual guidance, but probably hard to do so. I wish I knew of a good "how to write sound unsafe
code for beginners" guide, but I don't offhand. Sounds like a good future project for the OpSem team.
There are some relatively innocuous uses of unsafe
-- "hey I just want to get_unchecked
in my hot loop" -- but the memory safety bits are quite challenging I feel. (Too challenging, the language or tooling has room to grow here.) The invariants are not even fully decided, to boot.