For the record, I'm also in support of a compile-time check for this particular pattern, since for arrays it can trivially be checked just by looking at the type. I'm just saying that in the general case, the compiler shouldn't have to perform an arbitrary amount of static analysis (e.g., eagerly evaluating 9usize.wrapping_add(1)) just to catch suspicious patterns for its lints. Instead, the guaranteed way to enforce statically-checked preconditions in Rust is either to check them during constant evaluation[1] or to encode them on the type level; I demonstrated the former with my silly index!() macro.
even a post-monomorphization error can be better than nothing ↩︎
I think someone else has pointed it out before but dereferencing Box as well as indexing arrays does not actually use the trait, but it's a built in operation as far as I'm aware. Also Box::new should be irrelevant to this behavior. As I have pointed out somewhere above, considerations such as "Box::new could panic" (if that's even the problem you were considering) don't actually appear relevant to the unconditional_panic lint in other cases either, since conditionally reachable code is linted in the same way, AFAICT.
Could you please stop drawing incorrect conclusions, and perhaps ask first?
What gave you that impression that my only goal is to report a problem?
I asked the question here in order to raise my concern about the current behavior and perhaps learn why is so. Never I indicated that I as my only goal have reporting a problem.
Thank you for that clarification. I somehow missed it (just now went through all my replies to admire the logic and rhetoric applied and I found your answer :). Thanks.
Btw, would you know how to distinguish true compilation error from those caused by linting?
It's actually slightly less trivial than one might hope, but the telltale sign is, e. g. looking a the error produced by the example in the 3rd post in this thread
error: this operation will panic at runtime
--> src/main.rs:7:17
|
7 | let arr_v = arr_0[11];//Doesn't compile
| ^^^^^^^^^ index out of bounds: the length is 10 but the index is 11
|
= note: `#[deny(unconditional_panic)]` on by default
the note: `#[deny(unconditional_panic)]` on by default part is what's telling you that this error is actually just a deny by default lint, not a hard error. This note will, AFAIR, also only fire once for each category of lint, so if there's multiple unconditional_panic errors only one will contain this note - but even in that case, hopefully all of them will share a similar or maybe even the same error message "this operation will panic at runtime", so you can still identify them.
Note that researchers are interested in the sort of compilers that can do this analysis - where they evaluate your program at compile time to the point where running the program does the minimum amount of work possible.
But this has been a research topic since (at least) the late 1960s, and the fact that we don't routinely use such compilers is an indication of how practical they are.
If that is true, then why are you suggesting people are willing to sacrifice memory safety, right there in the quoted text?
I am willing to believe you may have been confused. Accessing beyond array bounds in Rust is not memory unsafe and not UB. It is prevented at runtime via a panic. In some cases the compiler can see when this would happen, but in the general case the runtime check is the best that can be achieved.
Nitpick: indexing is prevented. One might go for a raw pointers and offset them into the unallocated memory directly - in that case, of course, all bets are off, but, well, that's raw pointers for you.
I'd really prefer that we stay charitable here. There is obviously a valid issue with the compiler, we're just talking past each other regarding its consequences.
I'm afraid that I'm still having trouble understanding what you were trying to say. There are a few possible ways to handle out-of-bounds indices:
Specify that out-of-bounds indices panic at runtime. This method is used by the indexing expressions &array[i] and &mut array[i]. If i is a literal value, then the compiler has some code to output a helpful lint if this would unconditionally reach a panic. But this lint is somewhat inconsistent, and it will err on the side of not firing, since making it guaranteed to fire would require evaluating arbitrary constant expressions in the general case.
Specify that out-of-bounds indices cause a guaranteed compilation error. This method is used by &array[index!(i)] and &mut array[index!(i)], using my macro from earlier.[1] This has the downside that i must be a compile-time constant. Perhaps it would be helpful to add a similar API for infallible constant array indexing in the future.
Thus, when I say that extending the lint comes at a cost of compile time, I mean to say that it's already correct for indexing expressions &array[i] and &mut array[i] to panic at runtime, so it wouldn't make much sense for the compiler to perform an arbitrary amount of work to determine whether the panic is unconditional. Instead, if hard compile-time guarantees are what we want, then we should add them to a new const-based API, instead of overloading the existing panicking API. The unsafe, unchecked API can still exist on its own, indepentent from the panicking vs. const checking APIs.
It would probably be more practical to define an extension trait on [T; N] with a fn const_get<const I: usize>(&self) -> &T and fn const_get_mut<const I: usize>(&mut self) -> &mut T. But that would have the downside that every such error would be a post-mono error, whereas the macro solution could be modified to cause an pre-mono error if const {} blocks are stabilized. ↩︎
That's well and good, although to me the OP's posts show both a lack of understanding and a needlessly combative attitude. I'll gladly leave you to it. You definitely have more patience than I.