Most active commenters
  • JoshTriplett(6)
  • veber-alex(4)
  • tialaramex(3)
  • Rusky(3)

←back to thread

517 points bkolobara | 41 comments | | HN request time: 2.306s | source | bottom
1. veber-alex ◴[] No.45042068[source]
I find the Zig example to be shocking.

It's just so brittle. How can anyone think this is a good idea?

replies(5): >>45042182 #>>45042784 #>>45043157 #>>45045393 #>>45046983 #
2. zparky ◴[] No.45042182[source]
I didn't even see the error after the first glance until I read your comment.
replies(2): >>45042277 #>>45043125 #
3. veber-alex ◴[] No.45042277[source]
Yeah, I had to do a double take while reading the article because I missed the error completely while looking at the code.
4. tialaramex ◴[] No.45042784[source]
I assume it's a bug. However because this is an auteur language if you want the bug fixed it will be important to ensure the auteur also thinks it is a bug. If they get it into their head that it's supposed to be like this, they'll double down and regardless of how many people are annoyed they're insist on it.
replies(5): >>45043413 #>>45043564 #>>45044228 #>>45048414 #>>45054675 #
5. WD-42 ◴[] No.45043125[source]
I saw this comment first and then read the zig and still couldn’t find the error until I read the explanation below it. This has to be a bug.
6. kouteiheika ◴[] No.45043157[source]
Every language has questionable design decisions that lead to brittle code, although some more than others.

Like, how can anyone think that requiring the user to always remember to explicitly write `mutex.unlock()` or `defer mutex.unlock()` instead of just allowing optional explicit unlock and having it automatically unlock when it goes out of scope by default is a good idea? Both Go and Zig have this flaw. Or, how can anyone think that having a cast that can implicitly convert from any numeric type to any other in conjunction with pervasive type inference is a good idea, like Rust's terrible `as` operator? (I once spent a whole day debugging a bug due to this.)

replies(2): >>45043445 #>>45050774 #
7. veber-alex ◴[] No.45043413[source]
Knowing how the Zig developers operate it's 100% not a bug and it's exactly the case you described.
8. veber-alex ◴[] No.45043445[source]
You are right, but it doesn't mean we can't complain about it :)

As a side note, I hate the `as` cast in Rust. It's so brittle and dangerous it doesn't even feel like a part of the language. It's like a JavaScript developer snuck in and added it without anyone noticing. I hope they get rid of it in an edition.

replies(3): >>45043464 #>>45043857 #>>45045791 #
9. JoshTriplett ◴[] No.45043464{3}[source]
> As a side note, I hate the `as` cast in Rust. It's so brittle and dangerous it doesn't even feel like a part of the language. It's like a JavaScript developer snuck in and added it without anyone noticing. I hope they get rid of it in an edition.

Rust language hat on: I hope so too. We very much want to, once we've replaced its various use cases.

replies(2): >>45043819 #>>45044497 #
10. ozgrakkurt ◴[] No.45043564[source]
Tbh you would just use ErrorTypeName.ErrorKind to check equality and not error.ErrorKind if you are worried about this.

It is a tradeoff between making some things easier. And probably compiler is not mature enough to catch this mistake yet but it will be at some point.

Zig being an auteur language is a very good thing from my perspective, for example you get this new IO approach which is amazing and probably wouldn’t happen if Andrew Kelley wasn’t in the position he is in.

I have been using Rust to write storage engines past couple years and it’s async and io systems have many performance mistakes. Whole ecosystem feels like it is basically designed for writing web servers.

An example is a file format library using Io traits everywhere and using buffered versions for w/e reason. Then you get a couple extra memcopy calls that are copying huge buffers. Combined with global allocation everywhere approach, it generates a lot of page faults which tanks performance.

Another example was, file format and just compute libraries using asyncio traits everywhere which forces everything to be send+sync+’static which makes it basically impossible to use in single thread context with local allocators.

Another example is a library using vec everywhere even if they know what size they’ll need and generating memcopies as vec is getting bigger. Language just makes it too easy.

I’m not saying Rust is bad, it is a super productive ecosystem. But it is good that Zig is able to go deeper and enable more things. Which is possible because one guy can just say “I’ll break the entire IO API so I can make it better”.

replies(1): >>45044312 #
11. bigstrat2003 ◴[] No.45043819{4}[source]
As a Rust user, I hope that "as" never goes away. It's way too useful to get rid of, e.g. deliberately truncating numbers to a smaller number of bits. Can you do that in other ways? Absolutely. But they are all less ergonomic than "as". At most I would be ok with restricting "as" to an unsafe block, though that isn't a perfect solution because unsafe isn't really meant to apply to such cases.
replies(1): >>45044125 #
12. jcranmer ◴[] No.45043857{3}[source]
As much as I hate 'as' and try to avoid it, it also covers several things that are impossible otherwise (integer <-> float casts are impossible without it, e.g.). I've found that sometimes just being able to express a coerce-to-type-damn-the-consequences is useful.

Another painful bugbear is when I'm converting to/from usize and I know that it is really either going to be a u64 or maybe u32 in a few cases, and I don't care about breaking usize=u128 or usize=u16 code. Give me a way to say that u32 is Into<usize> for my code!

13. JoshTriplett ◴[] No.45044125{5}[source]
I'm hoping that we provide ergonomic alternatives for all the individual use cases. The brevity of `as` is a major reason we haven't removed it yet.

We have `.into()` for lossless conversions like u32 to u64.

We need to fix the fact that `usize` doesn't participate in lossless conversions (e.g. even on 64-bit you can't convert `usize` to `u64` via `.into()`).

We need to fix the fact that you can't write `.into::<u64>()` to disambiguate types.

And I'm hoping we add `.trunc()` for lossy conversion.

And eventually, after we provide alternatives for all of those and some others, we've talked about changing `as` to be shorthand for `.into()`.

replies(4): >>45045654 #>>45045700 #>>45047402 #>>45048310 #
14. jibal ◴[] No.45044228[source]
Why would you assume that? It's very intentional design decision.
replies(1): >>45050386 #
15. tialaramex ◴[] No.45044312{3}[source]
> if you are worried about this

Obviously nobody knows they've made this mistake, that's why it is important for the compiler to reject the mistake and let you know.

I don't want to use an auteur language, the fact is Andrew is wrong about some things - everybody is, but because it's Andrew's language too bad that's the end of the discussion in Zig.

I like Rust's `break 'label value`. It's very rarely the right thing, but sometimes, just sometimes, it's exactly what you needed and going without is very annoying. However IIUC for some time several key Rust language people hated this language feature, so it was blocked from landing in stable Rust. If Rust was an auteur language, one person's opinion could doom that feature forever.

16. 1718627440 ◴[] No.45044497{4}[source]
How are people supposed to use a language, that changes syntax?
replies(1): >>45044618 #
17. sunshowers ◴[] No.45044618{5}[source]
Rust's edition system allows the language frontend to be changed every few years.
replies(1): >>45045205 #
18. JoshTriplett ◴[] No.45045205{6}[source]
In particular, note that new Rust compiles code with both old and new editions. Upgrading Rust does not require you to move to the new edition. And one project can pull in crates from multiple editions.

(Imagine if Python 3 let you import Python 2 modules seamlessly.)

replies(1): >>45045661 #
19. Phil_Latio ◴[] No.45045393[source]
I would've assumed the error set is generated based on function signatures. Sick stuff.
20. Rusky ◴[] No.45045654{6}[source]
A method call like `.trunc()` is still going to be abysmally less ergonomic than `as`. It relies on inference or turbofish to pick a type, and it has all the syntactic noise of a function call on top of that.

Not to mention this sort of proliferation of micro-calls for what should be <= 1 instruction has a cost to debug performance and/or compile times (though this is something that should be fixed regardless).

replies(2): >>45046657 #>>45046754 #
21. 1718627440 ◴[] No.45045661{7}[source]
That's nice, but I think at some point they will cut the old implementation, as otherwise they would end with hundreds of versions.

And it doesn't exactly help to compile newer software on an older OS.

replies(2): >>45045713 #>>45046004 #
22. ChadNauseam ◴[] No.45045700{6}[source]
> We need to fix the fact that you can't write `.into::<u64>()` to disambiguate types.

This confused me too at first. You have to do `u64::from(_)` right? It makes sense in a certain way, similar to how you have to do `Vec::<u64>::new()` rather than `Vec::new::<u64>()`, but it is definitely more annoying for `into`.

replies(1): >>45046726 #
23. sunshowers ◴[] No.45045713{8}[source]
No, there's a commitment to not cut old editions. A new edition/frontend comes out every 3 years, so Rust (and maybe humanity) is probably going to be completely dead long before it has hundreds of versions.
24. Starlevel004 ◴[] No.45045791{3}[source]
As someone working with bitfields and enums-to-ints a lot, you can take ``as`` out of my cold dead hands.
25. steveklabnik ◴[] No.45046004{8}[source]
To elaborate on what sunshowers said, because editions only impact the early parts of the compiler, most of the code isn’t edition specific, which makes maintenance easy.
26. int_19h ◴[] No.45046657{7}[source]
The compiler doesn't have to implement a call as a call; having "magic functions" calls to which are special-cased by the code generator is an old and time-honored tradition.
replies(1): >>45046828 #
27. JoshTriplett ◴[] No.45046726{7}[source]
Yeah, you either need `u64::from` or `let x: u64 = expr.into()` or similar.

It does "make sense" but it's obnoxious, and we should have something better.

replies(1): >>45048428 #
28. JoshTriplett ◴[] No.45046754{7}[source]
> A method call like `.trunc()` is still going to be abysmally less ergonomic than `as`. It relies on inference or turbofish to pick a type, and it has all the syntactic noise of a function call on top of that.

If `as` gets repurposed for safe conversions (e.g. u32 to u64), there's some merit to the more hazardous conversions being slightly noisier. I'm all for them being no noisier than necessary, but even in my most conversion-heavy code (which has to convert regularly between usize and u64), I'd be fine writing `.into()` or `.trunc()` everywhere, as long as I don't have to write `.try_into()?` or similar.

> Not to mention this sort of proliferation of micro-calls for what should be <= 1 instruction has a cost to debug performance and/or compile times (though this is something that should be fixed regardless).

I fully expect that such methods will be inlined, likely even in debug mode (e.g. `#[inline(always)]`), and compile down to the same minimal instructions.

replies(1): >>45046825 #
29. Rusky ◴[] No.45046825{8}[source]
> I'd be fine writing `.into()` or `.trunc()`

Yes, this is specifically what I'm disagreeing with.

> I fully expect that such methods will be inlined, likely even in debug mode (e.g. `#[inline(always)]`), and compile down to the same minimal instructions.

That's the cost to compile time I mentioned.

replies(1): >>45047031 #
30. Rusky ◴[] No.45046828{8}[source]
Yes, that's how it should work. It is not how it works in today's rustc.
31. geysersam ◴[] No.45046983[source]
Can't a linter catch that you're referring to an error that doesn't exist anywhere else in your system and warn you about that and suggest you use switch instead of if?
32. JoshTriplett ◴[] No.45047031{9}[source]
Many things in the language theoretically go through a trait as well, except that we have special cases in the compiler to handle those traits more efficiently. If this were a performance issue, there's no reason we couldn't do the same for `.trunc()` or `.into()`.
33. muvlon ◴[] No.45047402{6}[source]
> We need to fix the fact that you can't write `.into::<u64>()` to disambiguate types.

Yes, that would be great. In the meantime, if you can't wait but want something like this, you can DIY it via an extension trait.

It's very easy to write it yourself, this is all it takes:

  pub trait To {
      fn to<T>(self) -> T where Self: Into<T> {
          <Self as Into<T>>::into(self)
      }
  }
Now whenever this trait is in scope, you get to simply do .to::<u64>() and it does exactly what Into does. If you prefer adding a tiny dependency over copy-pasting code, I've also published a crate that provides this: https://crates.io/crates/to_method
34. tczMUFlmoNk ◴[] No.45048310{6}[source]
> And eventually, after we provide alternatives for all of those and some others, we've talked about changing `as` to be shorthand for `.into()`.

Whoa, that could be awesome. It's always felt a bit unfortunate that you can't write `val.into::<SomeExplicitType>::()`—because the type parameter is on the trait, not the method. Of course, `SomeExplicitType::from` works, but sometimes that slightly upsets the flow of code.

Having just `val as SomeExplicitType` might be really nice for that common case. I do wonder if it'd feel too magic… but I'm optimistic to see what the lang team comes up with.

35. Ericson2314 ◴[] No.45048414[source]
I'm smirking big time reading this :), well said

(To be clear to others, it's not even that this is 100% a bad thing, but people love to shit on "design by committee" so much, it helps to have a bit of the opposite)

36. estebank ◴[] No.45048428{8}[source]
If only we had type ascription on expressions... ducks
37. tialaramex ◴[] No.45050386{3}[source]
Because it's a foot gun. People are going to shoot themselves in the foot with this feature, and I'm generous enough to assume that was not desired which makes it a bug.
replies(1): >>45050559 #
38. jibal ◴[] No.45050559{4}[source]
As others have noted, this is not idiomatic usage. But again, this is the design ... there's no bug. Languages are full of design decisions that may not be optimal in all usages ... that's different from bugs. Footguns breed bugs.

Here's a page of non-bugs: https://www.reddit.com/r/ProgrammingLanguages/comments/1hd7l...

39. vjerancrnjak ◴[] No.45050774[source]
I would like a language where your call stack can't be bigger than two/three.

You can call functions inside your function Main, but these function can't call any functions anymore (exception being flat helper functions defined inside your function).

I think it would save a huge chunk of time by just having all programs really nice and flat. You'd naturally gravitate towards mechanisms that make programs flat.

replies(1): >>45051874 #
40. dboreham ◴[] No.45051874{3}[source]
Also allows founding of: The Flat Program Society.
41. gwenzek ◴[] No.45054675[source]
Andrew actually agrees with the general sentiment. And this will be made into a compile error.

What's happening is that compiler knows the two errors come from disjoint error set, but it promotes them both to anyerror

Details at https://github.com/ziglang/zig/issues/25046