Live data from Hacker News

An incoherent Rust

boxyuwu.blog

171–175 of 175 posts

Re: An incoherent Rust

#171

Earlier quoted context omitted.

> I could have written it as x = bit_cast (char{2}), but does it really make a difference? Not really, that's also a variable. We're running into concrete differences here, which is what I was gesturing at. In C++ you've got two different things, one old and one new, and the new one does some transmutations (and is usually constexpr) while the old one does others but isn't constexpr. It's not correct to say that rein…

Is there a reason Rust would not (as it was done in the ‘good ole days’) index the table via pointer arithmetic from .data? Also, I’m assuming that because you are discussing new devs, that they are not making the implementation decision to place the table on the heap and using Rist’s subscript operator, which I would understand Rust not doing as default. I can not think of a reason that the table should ever be put…

The table is on the stack because we conjured into existence a temporary, so it's exactly as if the programmer had conjured the variable as a local by hand. Suppose our table is named SOUP

    const SOUP: [f32; 1000] = [ /* whatever */ ];
    let foo = something(blah_blah, blah) * SOUP[4];
    // The optimiser will see that SOUP[4] is exactly say 1.5_f32 so it'll just do
    // the same as if we'd calculated something(blah_blah, blah) * 1.5_f32
However

    let foo = something(blah_blah, blah) * SOUP[blah];
Now blah is a variable, we might need any value from SOUP, the optimiser doesn't know what values it might have - so a temporary is conjured into existence, equivalent to:

    let tmp = SOUP;
    let foo = something(blah_blah, blah) * tmp[blah];
You do presumably recognise that this now puts SOUP on the stack right? The temporary is equivalent, but without the explicitness.

Now if you know what you're doing you would of course have one single place in your program where you do this:

    static SOUP: [f32; 1000] = [ /* whatever */ ];
And now there's an immutable global named SOUP and like "the good ole days" we don't keep writing this huge data blob to the stack only to subsequently look at a single element. But that's not the thing the noob wrote so that's not what they get.

"Sufficiently smart compilers" are a very gradual miracle. In theory a compiler could realise this is a good idea, in practice today I doubt you will find such a compiler, so just write explicitly that you want a single variable if that's what you want.

Re: An incoherent Rust

#172

Earlier quoted context omitted.

> I could have written it as x = bit_cast (char{2}), but does it really make a difference? Not really, that's also a variable. We're running into concrete differences here, which is what I was gesturing at. In C++ you've got two different things, one old and one new, and the new one does some transmutations (and is usually constexpr) while the old one does others but isn't constexpr. It's not correct to say that rein…

> https://cpp.godbolt.org/z/EYnWET8sT I'm afraid that just C++ being C++ and you are deep into UB; you can't really modify a constexpr value at runtime, and if you cast away its constness with what is effectively a const cast you are on your own. This will print "0 3" which is obviously nonsense: constexpr int x = 3; ((int&)x) = 0; char y[x]; std::print("{}, {}",x, sizeof(y)); The output might change according to the…

> I'm afraid that just C++ being C++ and you are deep into UB

Of course, but the reason to even do this is only to illustrate that it's just another variable, nothing more. As much for myself as for you.

I've heard the stories about older languages but I assume that's not a thing on a modern Fortran and I'm sure we agree it's a bad idea.

The main thrust of this sub-thread was that languages can, and I believe Rust did, choose to solve the same issues but in a better way and so "This is too complicated in C++" doesn't translate to "It will be too complicated in every language". I think some C++ people have a version of the "End of History" nonsense, which is a nineteenth century idea. If you think noteworthy change happened after that and so history didn't end then hopefully you agree that makes no sense for general world history, and perhaps you can agree likewise C++ isn't the final apex of programming languages.

Re: An incoherent Rust

#173

Does the author expect incumbents to relax language rules that grant exorbitant privilege to incumbents? The orphan rule is up there with error handling in ways Rust is a screwed up language that appeals to people who don't know what they're missing. Other systems languages aren't weak in these ways.

Could you elaborate on that error handling part? To me, Rust is the only sane language I've worked with that has error-like propagation, in that functions must explicitly state what they can return, so that you don't get some bizarre runtime error thrown because the data was invalid 15 layers deeper

I don't know what quotemstr was specifically talking about, but here's my own take.

The ideal error handling is inferred algebraic effects like in Koka[1]. This allows you to add a call to an error-throwing function 15 layers down the stack and it's automatically propagated into the type signatures of all functions up the stack (and you can see the inferred effects with a language server or other tooling, similar to Rust's inferred types).

Consider the following Rust functions:

    fn f1() -> Result {...}
    fn f2() -> Result {...}
    fn f3() -> Result {...}
    fn f4() -> Result {f1()?; f2()?;}
    fn f5() -> Result {f1()?; f3()?;}
    fn f6() -> Result {f4()?; f5()?;}
Now, how do you define E4, E5 and E6? The "correct" way is to use sum types, i.e., `enum E4 {E1(E1), E2(E2)}`, `enum E5 {E1(E1), E3(E3)}` and `enum E6 {E1(E1), E2(E2), E3(E3)}` with the appropriate From traits. The problem is that this involves a ton of boilerplate even with thiserror handling some stuff like the From traits.

Since this is such a massive pain, Rust programs tend to instead either define a single error enum type that has all possible errors in the crate, or just use opaque errors like the anyhow crate. The downside is that these approaches lose type information: you no longer know that a function can't return some specific error (unless it returns no errors at all, which is rare), which is ultimately not so different from those languages where you have to guard against bizarre runtime errors.

Worse yet, if f1 has to be changed such that it returns 2 new errors, then you need to go through all error types in the call stack and flatten the new errors manually into E4, E5 and E6. If you don't flatten errors, then you end up rebuilding the call stack in error types, which is a whole different can of worms.

Algebraic effects just handle all of this more conveniently. That said, an effect system like Koka's isn't viable in a systems programming language like Rust, because optimizing user-defined effects is difficult. But you could have a special compiler-blessed effect for exceptions; algebraic checked exceptions, so to speak. Rust already does this with async.

[1] https://koka-lang.github.io

Re: An incoherent Rust

#174

Great write up of a problem that I'm glad Golang sidesteps The problem with this is that it's systemic and central to Rusts trait-based ecosystem composition. Go’s has a version but it's much smaller and more local. In Go, consumer-defined structural interfaces remove most of the pressure that causes the Rust problem in the first place which is producer led.

Sidesteps by not providing the same level of functionality. As an analogy, it would be equivalent to say that "contrary to an airplane, a car sidesteps the problem of requiring wings". Yes, indeed - but it doesn't fly.

Except writing rust doesn't feels like flying, it feels like falling.

Re: An incoherent Rust

#175

Earlier quoted context omitted.

Sidesteps by not providing the same level of functionality. As an analogy, it would be equivalent to say that "contrary to an airplane, a car sidesteps the problem of requiring wings". Yes, indeed - but it doesn't fly.

Except writing rust doesn't feels like flying, it feels like falling.

One could argue flying is just controlled falling.
Post reply on HN