Is there a way to allow indexing `Vec` by `i32` in my program?

I do not fully understand the security consideration. Implementing a trait for a particular type would only change behaviour of that type in my own program. Why does it considered a vulnerability?

No. It may just be a habbit to use int for all in Cpp solutions.

Signed/unsigned is not really a source of problem. I might be using u8 and still not able to index Vec with it.

I probably don't fully understand the orphan rule, but as far as I understood it, it was also a way to avoid ambiguity and breaking changes.

If Rust allowed you to impl Index<i32> for Vec in your own crate - what if the next version of the Rust standard library added its own impl Index<i32> for Vec which did something completely different? Which one would your crate now use?

I believe upcoming specialization work might have implications for this, but I'm not familiar with any of it.

1 Like

This is true. The broader point though was that if your number is an index, then it should be a usize. That's more or less what the type system is there for. The cast is the price you pay for converting whatever quanity you have into an index.

That said, as far as I'm aware, there's no particular reason why Vec shouldn't be indexable by any unsigned type (other than the complexity it would add implementing it).

2 Likes

I agree. My be I should think of indices as not just a numbers. I'll try.

2 Likes

Is there a RFC or discussion for adding implementations of Index and IndexMut for other numeric types? I've seen only this. For unsigned types it's logical to be able to index slices (u64 on 32-bit machine should be covered by usual checks), and for signed we can just panic on negative values. In some my programs I have a noticeable amount of as usize boilerplate (I work with u8 and u16 indexes to save memory), which I would be happy to remove.

1 Like

For what it's worth, you can get around the orphan rule by wrapping Vec in a "newtype" like struct MyVec<T>(Vec<T>) and then implementing the traits you want for that type instead.

Yes, but then you should always use my_vec.0 for acessing internal Vec:

let mut v = MyVec::<i32>(vec![]);
v.0.push(2);

which is even worse than using as usize :frowning:

If you implement Deref and DerefMut, method calls should transparently drop down to the inner Vec.

1 Like

For those interested, I implemented a proof-of-concept demo program, which allows MyVec indexing with i32: CODE HERE.

MyVec is still lack some Vec features. E.g. it can not be iterated using for i in &my_vec.

There is a reason why you should use an unsigned type here (See the answer from Michael!)
You just cast the i32 to usize which will do very strange things when you try to Index a negativ value...
I will repeat: DON'T DO THIS
for Real, stick to the given API, it was designed on purpose, so there are many hours of thinking and implementing involved.

My advise is: don't use the i32 in first place. Go the Rust way, not the C++ one.

5 Likes

To this, I would add that unlike in C/++, Rust's signed and unsigned integers should have identical performance characteristics because both are defined to wraparound on overflow (wheras in C/++ signed integer overflow is UB). So there is no reason to pick signed over unsigned when the problem domain does not dictate so.

3 Likes

This was not a UB warning, but a warning for a panic (because it wraps and so the Index will panic and he may doesn't know why)
In this case there is a dedicated problem domain namely, negative indexing is not plausible!

How is the negative indexing principally different from trying to index value outside of the vector/slice? In both cases we'll just panic. Meanwhile the recommendation "just use as usize" can lead to unexpected results.

1 Like

Let's assume you have a function that will do fancy stuff with a number. But for all numbers less than 0, it will produce some garbage.
Why you should use a signed type here? Right, you shouldn't. You are eliminating approx 2x10e9 values which will produce garbage at compile time! This is what you always should prefer, no matter what the language is (even in C++, Java, Go, ...). I you define something that will only work with positive numbers, just take an unsigned type. Simple as that.

Let me explain it further. What @907th does atm is &self.0[index as usize] which will convert e.g. -1 to 18446744073709551615. Good luck finding that number in your code, when it panics. You simply can't, because it isn't there.

1 Like

And what about using some unsigned integer like u8, u16, u32 for indexing?
would an RFC in this sense be reasonable?

5 Likes

Seems like a more reasonable idea indeed! :slight_smile:

There is at least one, which is that right now using something for indexing tells inference "BTW, this is a usize†", and it's likely that allowing more types there would break some existing programs. That's allowed breakage, but not something that people really want to do, so I think it'll end up blocked on some kind of "this is usize unless you're really sure it's not" feature, but that's hard.

Well, or a Range<usize> or whatever, but the point still stands

2 Likes

It will not produce any garbage, it will panic, which will be indication of a logical bug, the same way as out-of-bound indexing. And casting signed integers to usize is more likely to produce garbage results than panicking.

Please, don't speak for everyone so categorically. While I agree that usage of signed integers for indexing is usually indication of a bad design, in some cases it can be convenient to keep cursor as a signed integer, e.g. if you'll often perform offset operations on it.

I'm afraid this will become OT quiet soon.

You didn't read my sentence. I said, let's assume a function. I am not saying let's use the index function of the vec. You get me? Good.

Only if your vec is filled with over 10e18 elements, which is not the case in 99,9999%, so alsmost never.

And again: it was meant as a reaction of the fictional function which procudes garbage with negative values, not the index function. Please read my sentence more carfully, sorry if I write over complicated sentences or garbage, english is not my native language.

But this has nothing to do with indexing? Offset is a completly different topic and yes, signed values are the right thing to use there.