Remix.run Logo
▲ hingler36 3 hours ago

I've always felt that parts of the Rust standard library to be weirdly un-rusty. Like indexing a vector panicking instead of returning a Some(T) in case the index is out of bounds. It's easy to avoid if you put the effort in of course, but it did always feel weird that one of the first data structures you learn has this behavior right out of the gate.

▲Chai-T-Rex 3 hours ago | parent | next [-]

That's not really a memory safety bug, due to the fact that it panics rather than doing something unsafe.

The `variable[i]` notation is intended to function like in other languages, where it returns an unwrapped value. It would be ugly, noisy, unergonomic, and annoying to have to properly handle `Some` (like in `variable[i].unwrap()`) every single time you ever used it. Imagine doing `c[i].unwrap()[j].unwrap() += a[i].unwrap()[k].unwrap() * b[k].unwrap()[j].unwrap();`.

If you really want the result to be wrapped in `Some`, you can use `variable.get(i)`.

▲cube00 3 hours ago | parent | next [-]

> That's not really a memory safety bug, due to the fact that it panics rather than doing something unsafe.

The ? operator can be nicer to read then all those .unwraps() as an alternative and doesn't panic.

I guess the concern with panics is you're not forced to think about how you want to handle them like you do when you get a `Result` or `Option` returned.

As a result it could be used as a DDOS against your app if it's network accessible.

▲aslilac 2 hours ago | parent | next [-]

gotta remember tho that ? didn’t exist in 1.0, and now we can’t break existing code

▲cowlevel 36 minutes ago | parent [-]

Is that Rust's guarantee? I know that Go avoids breaking changes but I thought they were normal in Rust.

▲chiffaa 9 minutes ago | parent [-]

Rust does VERY limited breaking changes, the largest one probably being the `!` type inference change that was very carefully vetted, deemed a necessity and saw contributions to fix the issue in all crates in the official registry

▲adwn 2 hours ago | parent | prev [-]

Applying the ? operator to an Option<_> doesn't work inside a function returning io::Result<_>, for example, and turning the None from an out-of-bounds vector access into some io::Result::Err usually wouldn't make sense.

I would go so far as to say that an out-of-bounds index is always a logic error (to be handled by panicking), not an input error (to be handled with Result). And if the out-of-bounds access was caused by invalid input, then logic error consists of not checking the input before indexing.

▲ablob 23 minutes ago | parent [-]

The former issue arises from post-hoc thinking. Of course it's hard to retrofit into the language, but I see no reason why there shouldn't be facilities for the ? operator to turn a None into an appropriate error type.

The latter is a subjective matter. There are reasons to never want a panic (hello there, Linux kernel) and reasons to do more sophisticated stuff (core dump, cleanup or notifications) before crashing. And maybe one thread shouldn't lead to process death. I think most of these decisions should be kept to the user's discretion.

▲rendaw 2 hours ago | parent | prev [-]

GP didn't say it was a memory safety bug.

▲namjh 26 minutes ago | parent | prev | next [-]

That's just a design decision orthogonal to memory safety:

For "fallible" variant indexing, you have `the_slice.get(index)`: https://doc.rust-lang.org/stable/std/primitive.slice.html#me...

Although its method signature is kind of cursed due to generics, it matches your requirement that allows graceful OOB handling.

If the default indexing behavior were like `.get()` it would be too cumbersome to express repeated use of indexing, or multi-dimensional array indexing, etc.

Also there is get_unchecked which is unsafe. Trivially you'll trigger OOB access when misused.

▲suresk 3 hours ago | parent | prev | next [-]

I think it is a reasonable tradeoff, especially since `get(i)` is available to do what you want. An array index oob error is usually a logic issue, not some recoverable error, and in a lot of cases you're doing it where it is impossible to be wrong, so it is a lot of error-checking (and giving up things like x[0] += 1) to have the Index trait return an Option instead of a direct reference.

▲cowlevel 37 minutes ago | parent | prev | next [-]

Wrapping everything in optional does lead to optional fatigue, and people just unwrap each optional they get since they know the index is inbounds, missing the one instance where it really is possible to get None.

Maybe optional fatigue is Rusty but it's not a good style in any language

▲saagarjha 2 hours ago | parent | prev [-]

> Like indexing a vector panicking instead of returning a Some(T) in case the index is out of bounds.

If you are doing this then you've made a mistake in your code. Rust is consistent in catching these early.

▲uecker 2 hours ago | parent [-]

There is also the idea that the language is so good because such things are caught at compile time via the type system. (But this is generally not done for the same reason it is not done in C.)

▲pjmlp 2 hours ago | parent [-]

As shown in PL/I or NEWP, C could have done it both ways, checked and unchecked when really required, e.g. PREFIX(SUBSCRIPTRANGE), PREFIX(NOSUBSCRIPTRANGE).

Likewise regarding the storage for the length, if one is willing to go the way C went down a few years later, declare a pointer and make the array storage be pointer based, e.g. BASED(MY_ARRAY_PTR).

▲uecker 2 hours ago | parent [-]

Can you explain what you mean by "make the array storage be pointer based"?

▲pjmlp 5 minutes ago | parent [-]

In PL/I you can declare arrays with a length descriptor, thus allocating storage for the length, like in mostly safe systems languages.

Alternatively when allocating the array storage define its access to be pointer based.

This means no length descriptor will be stored alongside the array, and the access to array elements will be done via the given pointer.

It is up to the programmer not to mess up then.

Unfortunately C authors never added a way to do the former, given their experience with PL/I and Multics.

I am aware we are finally going into that direction.