Live data from Hacker News

An incoherent Rust

boxyuwu.blog

131–140 of 175 posts

Re: An incoherent Rust

#131
post #15

This is one of the (several?) things that make me very worried about Rust long-term. I love the language, and reach for it even when it sometimes isn't the most appropriate thing. But reading some of the made-up syntax in the "Removing Coherence" section makes my head hurt. When I used to write Scala, I accepted the fact that I don't have a background in type/set/etc. theory, and that there were some facets of the la…

> But reading some of the made-up syntax in the "Removing Coherence" section makes my head hurt.

It's interesting to note the discrepancy between replies to this blog here and, say, lobste.rs (which is neutral to it).

Here it's very concerned about complexity, while on lobsters it's mostly about needing this feature - yesterday.

Re: An incoherent Rust

#132

Earlier quoted context omitted.

Newtypes aren’t hacks, they’re perfectly acceptable in my opinion. Especially if you’re willing to also use a crate like `derive_more`.

In my experience using newtypes like this causes a constant shuffle between the original type and the newtype. If a library exposes Foo and I wrap it in MyFoo implementing some trait, I need to convert to MyFoo everywhere the trait is needed and back to Foo everywhere the original type is expected. In practice this means cluttering the code with as_foo and as_myfoo all over the place. You could also impl From or Dere…

One strategy I like is to declare “view” types for serialization and deserialization, because you’re going to be doing that anyway if your serialized format is meant to be compatible across versions anyway.

Serde also comes with a bunch of attributes and features to make it easy to short-circuit this stuff ad hoc.

I know this only solves the serialization use case, but that seems to be where most people run into this.

Re: An incoherent Rust

#133
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.

Re: An incoherent Rust

#134
Also it's sort of amazing how few people modify their tools and remove the objectionable bits.

For example, Java. Checked exceptions. Everyone hates checked exceptions. They're totally optional. Nothing in the JVM talks about a checked exception. Patch javac, comment out the checked exception checker, and compile. Nothing goes wrong. You can write Java and not deal with checked exceptions.

Likewise, you can modify rustc and make it not enforce the orphan rule.

Too many people treat their tools as black boxes and their warts as things they must tolerate and not things they can fix with their own two hands without anybody's permission.

Re: An incoherent Rust

#135

Earlier quoted context omitted.

Use static analyzers and move on. Almost all the complaints I see about C++ nowadays are removed by max warning levels. Set them as error. Certainly initialization is the single most confusing feature in C++, I can give you that. But still doable with s few patterns to remember. And warnings always max level.

I still use Eclipse CDT and its static analysis is running in real time, as you type code, which is killer. Combined with Valgrind integration, I don't see myself moving on anytime soon.

Is Eclipse CDT still good these days? Wow did not hear of it for a while. I thought C++ support was not maintained anymore.

I use CLion mostly but I never stop coming back to Emacs+LSP.

And yes, the analysis is quite competitive tbh. People often talk about this weird thing or the other in C++ but the experience is quite better than what the ISO standard strictly has to offer.

Re: An incoherent Rust

#136

Earlier quoted context omitted.

First of all mem::transmute is like bit_cast (which works perfectly fine in constexpr context), not reinterpret cast. Second, this compiles just fine: constexpr int ivalue = 1; constexpr bool bvalue {ivalue}; This fails at compile time (invalid narrowing): constexpr int ivalue = 2; constexpr bool bvalue {ivalue}; Note we don't need bit_cast for this example as int to bool conversions are allowed in C++.

Surely "We have many different ways to do this, each with different rules" is exactly the point? C++ 20's std::bit_cast isn't necessarily constexpr by the way although it is for the trivial byte boolean transmutation I mentioned here. I see that C++ people were more comfortable with the "We have far too many ways to initialize things" examples of this problem but I think transmutation hits harder precisely because it…

bit_cast and reinterpret_cast do different things: one works at the value level, the second preserves address identity (and it is problematic from an aliasing point of view).

Not sure what any of this has to do with initialization though.

FWIW, the direct translation of your rust code is:

    constexpr char y = 2;
    constexpr bool x = std::bit_cast(y);
It fails on clang for y=2 and works for y=1, exactly like rust;

GCC produces UB for y=2, I don't know if it is a GCC bug or the standard actually allows this form of UB to be ignored at contexpr time.

What is the rust equivalent of reinterpret_cast and does it work at constexpr time?

edit: I guess it would be an unsafe dereference of a casted pointer. Does it propagate constants?

Re: An incoherent Rust

#137
post #83

I feel like encapsulation and composition are in strong tension, and this is one place where it boils over. I've written a decent bit of Rust, and am currently messing around with Zig. So the comparison is pretty fresh on my mind: In Rust, you can have private fields. In Zig all fields are public. The consequences are pretty well shown with how they print structs: In Rust, you derive Debug, which is a macro that impl…

> so if the API is bad, you're pretty screwed. Is this really that big a downside? It encourages good APIs. The alternative of everything being public is the kind of feature that quickly becomes a big disadvantage in larger systems and teams, where saying “just don’t footgun yourself” is not a viable strategy. If there’s a workaround to achieve some goal, people will use it, and you end up with an unmaintainable mess…

The problem is it only takes one bad or incomplete API needed for your specific use case. I ran into this a lot when I used cpal. For example, the data stream enum type (i16, u8, f32, etc) didn't have Hash or Eq derived, so I had to create a wrapper class for the data stream type. But, the type was marked non exhaustive, so I wouldn't be able to tell if my wrapper would get out of sync with theirs. It was a pain to work around.

In other cases, I couldn't work around, so I had to vendor some things. I ended up implementing my own graph library, because the existing one wouldn't let me reach into change some invariants for undo/redo. Which I mean, fair enough if that's what's needed, but it's a real pain to reimplement something because the API was incomplete. And of course, now if something from another library needs petgraph, I'd have to convert my graph to its graph.

So yes, in theory, if we had great APIs this wouldn't be a problem. Unfortunately, APIs are always a work in progress, and sometimes need escape hatches in order to send values between libraries.

Re: An incoherent Rust

#138
post #63

Earlier quoted context omitted.

can you elaborate on how have they contributed to the quality of the ecosystem?

I've never once pulled in a new dependency and had the program fail to compile just by virtue of that dependency being present [because both my code and the new code both impl'd the same trait on the same type in some other code]. Because that can't happen because of coherence. (Right?) It's so easy to forget about the problems we don't have because of the (good) choices people have made in the past.

> Because that can't happen because of coherence. (Right?)

yes

Through you still can run into it when unsafe is involved, e.g. C FFI/no_mange or ASM with non-mangled labels as they are globally unique. Through IMHO, it's not a common problem and has ways to make it very unlikely for the projects where it matters.

In the end if you pull in C-FFI code (or provide it) you do ope yourself up to C ABI specific problems.

Re: An incoherent Rust

#140

Earlier quoted context omitted.

Newtypes aren’t hacks, they’re perfectly acceptable in my opinion. Especially if you’re willing to also use a crate like `derive_more`.

In my experience using newtypes like this causes a constant shuffle between the original type and the newtype. If a library exposes Foo and I wrap it in MyFoo implementing some trait, I need to convert to MyFoo everywhere the trait is needed and back to Foo everywhere the original type is expected. In practice this means cluttering the code with as_foo and as_myfoo all over the place. You could also impl From or Dere…

honestly in my experience it rarely matters (if you care about stable APIs) as most types you want to have at an API boundary are written (or auto generated) by you

this leaves a few often small types like `DateTime`, which you can handle with serde serialization function overwrite attributes or automatic conversions not even needing new types (through some of this attributes could be better designed)

serde is not perfect but pretty decent, but IMHO the proc macros it provides need some love/a v2 rewrite, which would only affect impl. code gen and as such is fully backward compatible, can be mixed with old code and can be from a different author (i.e. it doesn't have the problem)

Anyway that doesn't make the problem go away, just serialization/serde is both the best and worst example. (Best as it's extremely wide spread, "good enough" but not perfect, which is poison for ecosystem evolution, worst as serialization is enough of a special case to make it's best solution be potentially unusable to solve the generic problem (e.g. reflections)).

Post reply on HN