I'm really surprised that this compiles

I think you misunderstood. You can NOT, in the general case, detect this at compile time. Deref is a trait, meaning its implementation can run arbitrarily complex code against any manner of backing data. You can not make this a compile time error except in the simplest of cases where the compiler can "see through" the deref to what is behind it. This would have the effect of making such code brittle, erroring or not depending on the complexity of the implementation.

Panicking at runtime is not a form of memory unsafety.

This is true.

This is false.

2 Likes

I never said that it is.

You did, actually. Expanding a bit the post of yours that @m51 quoted:

The alternative to the compile-time checks that @LegionMammal978 argues are too expensive is run-time checks that panic. Your response makes clear that you consider this alternative to be memory-unsafe, and it’s this mistaken understanding that @m51 and others have been responding to.

2 Likes

In that case let's expand it even further:

I argued that compiler has all the information necessary to perform that check in this situation (access out of bounds) at compile time. Never I've said that panicking is memory unsafe.
So you are both wrong: @m51 and @2e71828

If not that, what particular memory unsafety were you alluding to in your question? Or was it just an unfounded rhetorical attack on @LegionMammal978 ?

2 Likes

Is access out of bounds considered to be memory unsafe?

Everyone, please try to avoid fast back-and-forths, too short replies, or publicly addressing and discussing the way how things are said. Neither of these tends to make discussions more productive, on the contrary, there’s always the chance of derailing the thread.

When answering to multiple posts at the same time, it's often also considered good style to consolidate the reply into one larger reply with multiple quotes instead of creating lots of small replies.

4 Likes

Unprotected out-of-bounds access is UB, yes, but that’s impossible to do with the indexing operator []¹ in safe code. Slices, arrays, and vectors will all panic instead of performing the illegal access, so there’s no possibility of UB here whether or not the compiler allows the code to compile.

Because of this, there has never been any real safety issue anywhere in this thread. The compile error that you flagged in your original post is there for convenience only, which is why you are allowed to turn it off— The actual safety guardrails are somewhere else.

Extending the scope of that convenience check is certainly possible, but it’s relatively low priority. It’s provably impossible for it to be perfect, so there must always be another level of checks somewhere. A patch to make this check cover more cases would likely be accepted, but most Rust developers are focussed on improving other aspects of the compiler at the moment.


¹ Or any other way, either.

3 Likes

I hope you stop repeating the same arguments and making strawman argument.

  1. No one is saying they prefer runtime panic to compile error.
  2. No one is saying it is impossible to detect OOB access in this specific case.
  3. No one is saying we are sacrificing safety guarantees.
  4. No one is saying the status quo is perfect.

What we ARE saying are:

  1. No matter it's runtime panic or compile error, no actual runtime invalid access is happening.
  2. Rust's goal is preventing runtime invalid access, not making all dubious code compile error.
  3. Additional checks are welcome, but not a deal-breaker.
  4. Detecting OOB access in the general case is impossible.
  5. Detecting some is better than none.

And what's most important:

  1. Rust aims to be a tool used in actual industrial development, not some computer science theory "toy" language. You have to balance the user experience and the perfection level.
1 Like

Actually it is you who with your last reply makes a strawman argument. I never did that, but I'll be glad to be pointed out to the place you think I've made such argument.

That is the whole reason for this topic. I'm glad we agree on that.

Yes, sure. You said it's imperfect, we agree it's imperfect. We are just telling you the reasons why thing are what they are now. Then we are explaining why we are not treating it's imperfectness as a broken promise or something shakes the root.

Maybe you had the vision that Rust would be "the theoretically perfect language" and surprised by the fact it's not. We understand that. We all hope we can have that perfect thing somewhere sometime. It's just the reality sometimes forces the choice between the practicality and perfection. Yeah, it's somewhat sad, but it's not something would be changed by just pointing it out.

3 Likes

I didn't.

I'm surprised that is inconsistent in such basic scenario.

It's a result of multiple things combined.

  1. As said before, we accept inconsistency in lints.
  2. It's not that basic. At least in the general case.
  3. Lack of contributor time. There's not enough attention raised for this inconsistency, other things will be worked on first.

I think the best thing you can do now is implement this feature yourself and make a PR to rust-lang. And I'm sure everyone will thank you for that.

3 Likes

It's not so clear to me.

In order for a compile time check the compiler would have to analyse all the code behind the Box::new() function: Box in std::boxed - Rust

Then then it would have to analyse all the code behind the indexing operation 'ptr_0[11]', which is itself another function: Index in std::ops - Rust

Presumably these functions could be arbitrarily large and/or complex. A 100% certain analysis of which is not even possible. Or could take a long time when it is possible.

So, given the impossibility of checking this in the general case a run time check is used. Memory safety is assured, UB does not happen. Life is good.

It technically may be inconsistency in lints, but practically it is inconsistency in user experience.

I actually have zero will to work on somebody's else's project for free. I use Rust. Rust is not my project nor I have vested interest in that technology.
I simply pointed out some "faulty" behavior of this technology. Just because I did that it doesn't mean that I am willing to actually work on a fix.

That is incorrect. "All" the compiler needs to do is to deref to target. Which, as confirmed it has that information ready.

See array vs Box example, and consider the fact that compiler has all the info needed to deref to target during compilation time.

I have to admit that I don't know what goes on under the hood. But on the face of it, looking at the code in your OP, Box::new() is just a function, defined elsewhere, which may as well be implemented by myself. And the '[11]' is a method of a trait, defined elsewhere, that may as well be implemented by myself.

So, I conclude the information required to determine if that indexing is out of bounds is not present in the source that we see in your example. In order to determine if the indexing ptr_0[11] is out of bounds the compiler would have to analyse the guts of those functions/methods.

This analysis is not possible or not practical in general. And so we get a run time check.

Now, if there is something special about ´Box`that makes my reasoning above invalid perhaps someone could explain it to me.

In that case, thank you for the bug report. I have filed an issue about it so that the report doesn't get lost.

In future, if your only goal is to report a problem, please consider filing an issue directly— The general assumption when you post here is that you are seeking either understanding or help resolving a problem.

9 Likes

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.


  1. even a post-monomorphization error can be better than nothing ↩︎

1 Like

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.

2 Likes