Hacker News new | past | comments | ask | show | jobs | submit login

> starting with a C-like language as a base

Which C-like language do you believe was the base of Rust?

If you're judging because the syntax looks like a semi-colon language that's just a thin disguise, to make it easier to onboard people from those languages.






Not a specific C-like language, but that family of languages in general and the design decisions from them:

A number is just a number even if two numbers represent completely incompatible things. If one integer represents a chmod value and one represents a number of files then you can add them together and the compiler will do nothing to stop you even though adding the two is always nonsensical. There's ways around this by creating your own types, but it's not a trivial process. In Ada I can just declare two different numeric types with identical ranges (type A is range 1..200) and the two will be incompatible.

Somewhat related to the above, all array indexes are just numbers. In Ada we can define a type and then use that type as an index of an array type, so if a value is the index type then it must be a valid array index. In Rust if I declare an array as having 5 elements then pass it to a function where I try to access element 6 then nothing will try raise an error at compile time.

Continuing on from the last point, arrays are only indexed by numbers rather than any discrete type. In Ada and enumeration is a valid type to use as an array index, or a range from 5 to 15, or -123 to 7. I'm sure this is something you can do with a generic in Rust, but it's going to be more clunky than having native language support.

Structs are just basic collections of fields. In Ada we have discriminated records that allow for some struct fields which can only be set at instantiation to control things like the number of elements in an another field which is an array. An example of where this could be used is in a ring buffer which is fully contained within a simple struct without the need for extra memory allocation. (I'm aware this conflicts with the other examples about arrays, in short there's a second type of array declaration with a variable subrange as an index). Maybe you can do this with generics in Rust, but it's not as clean, especially if you want to, for example, add a procedure to the ring buffer that takes another ring buffer as a parameter.

These are just off the top of my head as someone who's only briefly looked at Rust, I'm sure there's many more examples. The exact syntax might not match C, but many of the concepts come directly for C and derivatives.

The Ada examples I've given exist in other languages too in various forms, I just happen to be an Ada dev so I know exactly how they work there.


You can do everything you're saying in Rust.

1. https://docs.rs/dimensioned/latest/dimensioned/

2. https://doc.rust-lang.org/std/ops/trait.Index.html

3. Maybe const generics? e.g., Foo<N: 3> where Foo { array: [u32; N] } or something. You could also do this with less typed-ness with Foo::new_with_capacity(n) and some private size management.

It definitely takes aesthetically from the C-family of languages in the same way that Java and Go are all braces and semicolon languages. I can't exactly minimize this - because I am comfortable with languages that look like this and less comfortable with some languages that don't, so I recognize the effect is very real - but it doesn't affect the features of the language much.


Well you say "that family" but you only give examples of why Rust isn't Ada.

Is the situation that you consider there are two families of languages, "Ada" and then "All of the other programming languages" and you've decided to label that second group C?

On that basis I agree, Rust has a "C base" as do Fortran, Logo and the Standard ML of New Jersey, but I don't think that's a useful way to understand anything about programming languages.

> In Rust if I declare an array as having 5 elements then pass it to a function where I try to access element 6 then nothing will try raise an error at compile time.

https://rust.godbolt.org/z/4eMTTMar4

Looks like a compile time error to me. I suspect you wrote a function which takes a slice reference not an array. In Rust these types are named &[T] and [T; N] and sure enough we don't know the value of N in a slice because well, it's not part of the type. Ada can't magically know this either, in Ada you've probably been writing an array here, and if that's what you meant you can do that in Rust too with similar effect.

You can't do it in C (the array types do exist in C, but they decay to pointers at the edge of any function, so we can't pass these types between functions) but you can in Rust.

> arrays are only indexed by numbers rather than any discrete type

I guess you're thinking about core::slice::SliceIndex<[T]> ? This is (at least for now and perhaps forever) an unstable trait, so, you're not "allowed" to go implement this in stable Rust, but if you did anyway you can cheerfully implement SliceIndex for your own type, Rust won't barf although you can probably undermine important safety features if you try because none of this is stabilized.

Far from being "only numbers" core::slice::SliceIndex<[T]> is already implemented for the ranges (both kinds) and for a binding pair (lower_bound, upper_bound)

So in terms that maybe seem more obvious to you, we can do foo[(Bound::Unbounded,Bound::Unbounded)] as well as foo[3..=4] and foo[3..] in addition to the obvious foo[3] -- these are all defined on slices rather than arrays though because in practice that's what you actually want and we can just decay the array to a slice during compilation.

Overall I still think it really is the syntax that threw you off.


> Well you say "that family" but you only give examples of why Rust isn't Ada.

Ada is the language I work with daily, but I think other languages that have been designed for the ground up for safety have similar features.

Aside from arrays vs slices, the solutions to these problems all seem less ergonomic than they could be if they were a part of the core design of the language, which is my major issue here rather than whether it is possible at all to do such things.

> You can't do it in C (the array types do exist in C, but they decay to pointers at the edge of any function.

You can pass around a pointer to an array in C (int (*)[3]), but I don't think it's worth considering as the language and standard library are not designed to make working with it easy. This is the same as my issues with Rust, if declaring a new type to represent a number of files isn't as easy as just using a u32 then no one will use it, etc..

My opinion is that the absolute safest way to do something needs to also be the easiest way to ensure that users always pick the safest option. Ideally the user would even have to go out of their way to pick the more unsafe option. Rust is really good at this with memory and thread safety, just not as much with the rest of the language (although it's certainly better than C).

As an addendum to my last example in the GP, I suppose the borrow checker prevents any safety issues from such a type requiring a separate allocation without generics that I was originally considering, although it does not help with leaks.


> This is the same as my issues with Rust, if declaring a new type to represent a number of files isn't as easy as just using a u32 then no one will use it, etc..

    #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
    #[repr(transparent)]
    pub struct FileCount(pub u32);
The annotations aren't needed until you make these part of your API and you want to communicate specific actions, but it's likely close what you'd end up with.

The first thing I'd do, and likely get away with, is

    struct FileCount(u32);
> My opinion is that the absolute safest way to do something needs to also be the easiest way to ensure that users always pick the safest option.

Agreed.


> Somewhat related to the above, all array indexes are just numbers.

Create your own type[0] encapsulating arrays, and then implement the Index[1] with a custom Idx generic.

> In Ada and enumeration is a valid type to use as an array index, or a range from 5 to 15, or -123 to 7

That's what TFA is about.

[0] https://doc.rust-lang.org/rust-by-example/generics/new_types... [1] https://doc.rust-lang.org/std/ops/trait.Index.html




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: