Readit News logoReadit News
DannyBee · a year ago
This seems not even close to the worst feature of rust - this seems like it needs some more design work and baking. Like lots of things.

The amount of hyperbole in this article makes it a bit hard to take the author all that seriously.

Is there evidence more baking won't happen? While i have my loves and hates about rust, it definitely always felt like they had a pretty thorough/careful process for additions like this. If you go constructively into the threads and offer some concerns, you will usually get some reasonable response.

(All processes of course, fail, so this is not always true, but it's mostly true)

While i think it's fine to write rants on blogs, and don't feel like everyone has a responsibility to file bugs or whatever before they write a rant about it, if you actually want to see this "worst feature" fixed, this probably won't help very much.

(IE You don't have to be constructive, or even helpful, but if you want to be constructive or helpful, this ain't how you do it)

dathinab · a year ago
> Is there evidence more baking won't happen?

No, actually there is a lot of evidence that it will still be worked on.

Normally I would just say look at the issue linked in the nightly docs but due to an overlap of tacking PR and moving it from std to core PR it's not supper useful.

Tracking Issue: https://github.com/rust-lang/rust/issues/78485

If the author has constructive critique they should probably mention it there (after skimming through the discussion to make sure this wasn't already considered and not done due to subtleties they overlooked in the blog post (like e.g. that it's a standard/core feature which has to work across all targets and as such can't rely on anything being initialized to 0 by the OS, or that depending on the global allocator used you definitely can't rely on things being zeroed even if the OS only hands out zeroed memory, etc. etc.))

Xunjin · a year ago
I'm not as much low-level development as the author seems to be, but the hyperbole made me think “you have a point, which is/can be valid, but aren't you stretching the reasons to fit in your sentiment/PoV?”

Being honest, plenty of times we throw “Ergonomics” as an argument, however, are ergonomics more a feeling of how good are the API usage instead of actually prove with examples and design choices?

thayne · a year ago
Well, as they said in the conclusion, they don't really have any constructive suggestions. It is mostly a lament that this wasn't better designed from the beginning, because backwards compatibility puts significant constraints on the design.
queuebert · a year ago
The more pertinent question to me is can we implement some new static analysis that understands buffer re-use and can hoist buffer initialization outside the loop? Rather than make the programmer write obfuscated code for efficiency, it is usually better to have the compiler do the heavy lifting.

P.S. Also, folks, don't re-use buffers without zeroing unless you absolutely need the performance and know what you're doing.

mrpf1ster · a year ago
Why would re-using a buffer be bad? Assuming you write to it with the contents of the file/stream before it is read.
kohbo · a year ago
You just answered your own question
phkahler · a year ago
And that's not something you should be depending on a compiler to verify.
vlovich123 · a year ago
I like that direction better but it requires the ability to declare data-flow based contracts whereas Rust’s tools are only lifetime and type contracts. Is there a language that has data-flow based contracts?
queuebert · a year ago
That would be easier but is not required. There are no compiler hints these days to unroll loops or hoist invariants, even though if done incorrectly it could change the result. It would take some complicated analysis, but I think it could be done safely in some cases.
ijustlovemath · a year ago
what do you mean by this?
jvanderbot · a year ago
Fair, but note there is a significant subset of Rust-targeted programmers who dislike the compiler doing things like that. They also dislike the compiler doing things like auto-initializing every loop iteration, but two wrongs wouldn't make it right, just less wrong.
Zagitta · a year ago
Maybe Rust needs another type of reference that's exclusive write only? Right now there's RO (&T) and exclusive RW (&mut T) but WO is missing.

Having a WO reference would allow these read_buf APIs to express they only write and never read so the uninitialized memory is safe to pass directly.

gpm · a year ago
In some sense that's exactly what a `&mut MaybeUninit<T>` is?
mmastrac · a year ago
Probably more once https://doc.rust-lang.org/beta/std/mem/union.MaybeUninit.htm... is no longer nightly-only.
1oooqooq · a year ago
everyone just tell you to use mpsr in this case
0x1ceb00da · a year ago
> Even an obvious optimisation of moving the buffer declaration outside of the loop isn’t available to the compiler.

Why? Can't the programmer just do this himself?

Arch-TK · a year ago
The compiler cannot assume that the read call won't read from the mutable reference (well, it might be able to given a sufficiently sophisticated optimizer and/or aggressive inlining).

The programmer, on the other hand, can do this, but the point is to make this implicitly possible by making it more explicit that read does not read from the buffer (and therefore allowing it to accept uninitialized memory).

dietr1ch · a year ago
And I don't think it can ensure that all the bits were written. I've been bit by people trying to reuse buffers/objects like this that were not fully rewritten in one of the possible re-uses. It's a bit puzzling how a change that just adds a new continue leads to memory corruption.
vlovich123 · a year ago
Because the compiler doesn’t know what read/write are doing to the buffer. And since it’s declared as [0; 4096], the compiler wouldn’t be able to do anything other than 0’ing the entire 4kib region on every read instead of what’s dirtied if it attempted to automatically hoist. BorrowedBuf is an attempt to let you declare [MaybeUninit::uninit(); 4096] which the compiler could hoist although there it doesn’t matter either since the allocation of the uninit array is just an adjustment of the stack pointer.
lmm · a year ago
> Why?

Because as far as the compiler is concerned it appears to change the behaviour, unless the compiler gets very fancy.

> Can't the programmer just do this himself?

Yes, but it's not really desirable for them to have to (and would arguably make the code less maintainable if they did). Doing the right thing should be easy.

Waterluvian · a year ago
Arguably the buffer belongs in the loop scope because it is only relevant there. It’s probably also safer from wrong use.

This feels like exactly what you want the compiler to think about: a case where optimization comes at the cost of organization.

immibis · a year ago
Organization is basically completely irrelevant for a small piece of code like this, which fits in your head all at once.
auggierose · a year ago
Not if they are using Rust ... which is why I am not.
dgacmu · a year ago
This is incorrect. It's trivial and compiles just fine. The argument here is that maybe for reasons the programmer doesn't want to - such as not wanting the buffer to outlive its use inside the loop, and they don't want to have to double-nest:

    { 
        let mut buf = [0; 4096];
        loop {
            ...
        }
    }
That accomplishes exactly the same goal but there's an argument -- not well made in the blog post -- that the compiler should be able to do some form of this hoisting automatically. In C, it would be automatic, because C doesn't make a zero-initialized promise for stack-allocated variables. In Rust it's not because the array is specified as zero-initialized. Of course, C's behavior comes with certain drawbacks of its own. ;)

Rust's behavior isn't unreasonable. It's just a potential missed optimization, but automating it is challenging.

vlovich123 · a year ago
What language would automatically be able to hoist the array outside the loop in that kind of code?
pnathan · a year ago
I think the answer is that in a case when you need that speed, you hoist the stack allocation & zeroing and unsafe that buffer in the loop if need be. Test well. I am a huge Rust fan but also it is actually possible to write correct unsafe code.

If I am interacting with from IO space, I would much rather write the interaction code myself for the machine at hand than farm it out to an array of third party crates. ::shrug::

getting the machinery to let it properly be hoisted smoothly and safely would be nice, but it isn't required.

personally I think rust macros are very painful and the "worst feature", but that's speaking as someone who did a fair bit of Common Lisp.

tcfhgj · a year ago
what's so bad about macros?
pnathan · a year ago
Have you ever used Common Lisp macros? Its absolutely eye poppingly easier.
umanwizard · a year ago
> While replacing the array of zeros by an array of uninitialised values may work in specific circumstances, the code is unsound. Change to the compiler, its options, modification of unrelated parts of the code or using the function for a different Read trait implementation may break the program in unpredictable ways.

Why? It seems the only thing on that list that will cause UB is using the function with a different reader (one that inspects the uninitialized bytes). Why would any of the other listed possible changes break it?

GrantMoyer · a year ago
Follow up to my earlier comment; The docs say creating a reference to uninitialized memory is undefined behavior, but I couldn't think of any cases where it would go wrong if the data is never actually accessed. It turns out that's because there currently aren't any. See comment [1] and the threads linked there.

Rust calls references to uninitialized memory undefined behavior because it wants to reserve the right to implement some kinds of optimizations using that fact in the future, however currently no optimizations do, and in fact the standard library depends on the compiler being well behaved here.

It's possible at some point in the future some optimization will be added which would cause problems for references to uninitialized memory, but it seems more likely that the requirements on references will eventually be relaxed instead.

[1]: https://github.com/rust-lang/rust/issues/119241#issuecomment...

GrantMoyer · a year ago
In Rust, creating a reference to uninitialized data is undefined behavior, even if you don't access the data through it[1]. The optimizer assumes all references are intialized.

[1]: https://doc.rust-lang.org/std/ptr/#pointer-to-reference-conv...

mmastrac · a year ago
This API has basically been adopted from Tokio. Like most of Rust buffer types, it's "not bad" to use as a caller and "awkward" to use as a consumer.

The pain of paying for buffer init is real, however. The last two projects have both seen perf hits from it.

Deleted Comment