- 
                Notifications
    
You must be signed in to change notification settings  - Fork 559
 
Add caveats about mutable references in consts #2058
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| ``` | ||
| 
               | 
          ||
| > [!NOTE] | ||
| > This is allowed as, for a value of a zero-sized type, no bytes can actually be mutated. We must accept this as `&mut []` is [promoted]. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that this is the first time in the reference that we document that mutable references to zero-length arrays can be promoted.
Note that this only works with zero-length arrays. Also, this promotion does not seem to always happen, see rust-lang/rust#140126
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this only works with zero-length arrays. Also, this promotion does not seem to always happen, see rust-lang/rust#140126
Look at the bottom note in https://doc.rust-lang.org/nightly/reference/const_eval.html#constant-expressions and rust-lang/rust#143129.
        
          
                src/items/constant-items.md
              
                Outdated
          
        
      | > This is allowed as it's separately not allowed to read from an external static during constant evaluation. See [const-eval.const-expr.path-static]. | ||
| > [!NOTE] | ||
| > We also disallow, in the final value, shared references to mutable statics created in the initializer for a separate reason. Consider: | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This preexisting note seems incorrect. It talks about mutable statics, and then shows an example with a reference to an interior-mutable temporary. Such temporaries do not live for 'static. And even if they were to, they would be a const-promoted thing, which is subtly different from statics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In an initializer such as,
const _: &AtomicU8 = &AtomicU8::new(0);the rules for "extending based on expressions" apply as follows:
- The initializer is an extending expression.
 - The borrow expression is therefore an extending expression, so the operand of the borrow is an extending expression.
 
The "operand of an extending borrow expression has its temporary scope extended".  I.e., we extend the temporary scope of AtomicU8::new(0).
How far do we extend it? "To the end of the program":
Lifetime extension also applies to static and const items, where it makes temporaries live until the end of the program.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also specify that interior mutable values are not subject to constant promotion.
https://doc.rust-lang.org/nightly/reference/destructors.html#constant-promotion
Promotion of a value expression to a
'staticslot occurs when the expression could be written in a constant and borrowed, and that borrow could be dereferenced where the expression was originally written, without changing the runtime behavior. That is, the promoted expression can be evaluated at compile-time and the resulting value does not contain interior mutability or destructors (these properties are determined based on the value where possible, e.g.&Nonealways has the type&'static Option<_>, as it contains nothing disallowed).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is still confusing to call this a "mutable static", which one could reasonably interpret as referring to a static mut or at least some sort of static item with mutability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Edit: GH didn't show me @RalfJung's comment; this was posted in parallel to it:)
All that said, I agree the wording was imperfect. I've pushed a commit to clarify the point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is still confusing to call this a "mutable static", which one could reasonably interpret as referring to a
static mutor at least some sort ofstaticitem with mutability.
Yes, agreed; this is avoided in the revision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we give the failing examples a rationale, too? The passing ones have rationales, but we don't explain why e.g. we don't want to allow creating references to interior mutable data from a const unless the reference is to a static item
2e7cb57    to
    7acafcd      
    Compare
  
    
      
        
              This comment has been minimized.
        
        
      
    
  This comment has been minimized.
          
 Sure. I've added some rationales. I'm curious, though, what would you say for this one? # #![allow(static_mut_refs)]
static mut S: u8 = 0;
const _: &dyn Send = &unsafe { &mut S }; // ERROR.
//                             ^^^^^^
// Not allowed as the mutable reference appears in the final value,
// even though type erasure occurs.(It's easier to say, for this sort of thing, why we allow something rather than why we don't, as we're conservatively only allowing the things that we have good reason to allow and where we can prove that it's OK to allow those things.)  | 
    
5cad100    to
    486075d      
    Compare
  
    | 
           Yea that case is just a limitation of the few provenance checks happening in CTFE. We do allow use std::sync::atomic::AtomicBool;
static S: AtomicBool = AtomicBool::new(false);
const _: &dyn Send = &unsafe { &S }; | 
    
As it turns out, the story that "the final value of a constant cannot contain any mutable references" has some nuance to it. Let's describe these caveats. Thanks to theemathas for noticing these and to RalfJ for filling in context.
We have a note that describes why, perhaps surprisingly, it's OK to have a reference to a static with an interior mutable value in the final value of a constant item but it's not OK if the interior mutable value is a temporary from the initializer. There was room to improve how this was worded, so let's take it.
We had described the design rationale for why certain values of constant items were accepted, but for the values that are not accepted, while we had described mechanically the reasons for this, we had not described the rationale for it. This can be a bit tricky, because often the real rationale is that "we're being conservative and only allowing the cases where we have a good reason to allow them and where we can prove that allowing them is OK". So it is easier to describe why we allow something than why we don't. But still, let's try to describe some reasons why we don't allow some things yet.
486075d    to
    53f4392      
    Compare
  
    | 
           This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.  | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
| ``` | ||
| 
               | 
          ||
| > [!NOTE] | ||
| > This is allowed as it's separately not allowed to read from a mutable static during constant evaluation. See [const-eval.const-expr.path-static]. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This note is a bit confusing since it does not relate back to what we said above about why these references are disallowed.
It should probably also say that this is sound to allow because it take an unsafe block to create a mutable reference to such a static, and that unsafe block carries the obligation of ensuring that no unsound aliasing will occur, Furthermore, the problem regarding "is there a new instance of the pointee for every use of the const or not" does not come up since unambiguously, these references all point to S.
Update books ## rust-lang/book 6 commits in af415fc6c8a6823dfb4595074f27d5a3e9e2fe49..f660f341887c8bbcd6c24fbfdf5d2a262f523965 2025-10-28 01:41:51 UTC to 2025-10-24 15:39:51 UTC - Update to Rust 1.90 (rust-lang/book#4545) - Update to Rust 1.89 (rust-lang/book#4544) - Update to Rust 1.88 (rust-lang/book#4543) - Update to Rust 1.87 (rust-lang/book#4542) - Update to Rust 1.86 (rust-lang/book#4541) - Remove unused subheadings (rust-lang/book#4538) ## rust-lang/edition-guide 1 commits in e2ed891f00361efc26616d82590b1c85d7a8920e..5c621253d8f2a5a4adb64a6365905db67dffe3a2 2025-10-23 14:13:01 UTC to 2025-10-23 14:13:01 UTC - Add back the link for never_type_fallback_flowing_into_unsafe (rust-lang/edition-guide#378) ## rust-lang/reference 12 commits in 752eab01cebdd6a2d90b53087298844c251859a1..e122eefff3fef362eb7e0c08fb7ffbf5f9461905 2025-10-28 20:52:27 UTC to 2025-10-21 19:42:06 UTC - Remove custom redirect scripts (rust-lang/reference#2065) - Avoid specifying "last import wins" for MBEs (rust-lang/reference#2057) - Add caveats about mutable references in consts (rust-lang/reference#2058) - Move punctuation index to an appendix, and expand for other syntaxes (rust-lang/reference#2061) - Use American spelling for "labeled" (rust-lang/reference#2066) - Remove rules from names intro (rust-lang/reference#2060) - Minor adjustments to inner/outer attribute definitions (rust-lang/reference#2059) - Change underscore to be a keyword (rust-lang/reference#2049) - Update `proc_macro_attribute` to use the attribute template (rust-lang/reference#1889) - Update `proc_macro` to use the attribute template (rust-lang/reference#1887) - Update `macro_use` to use the attribute template (rust-lang/reference#1886) - Update `macro_export` to use the attribute template (rust-lang/reference#1885) ## rust-lang/rust-by-example 7 commits in 2c9b490d70e535cf166bf17feba59e594579843f..160e6bbca70b0c01aa4de88d19db7fc5ff8447c3 2025-11-03 12:26:45 UTC to 2025-10-22 13:19:06 UTC - Fix typos in flow_control/match/binding (rust-lang/rust-by-example#1971) - add clarification on overflowing_literals lint (rust-lang/rust-by-example#1970) - Update enum_use.md (rust-lang/rust-by-example#1960) - Remove weird extra spaces in code (rust-lang/rust-by-example#1962) - Include a link to The Rust Reference in flow_control/match/destructuring (rust-lang/rust-by-example#1963) - Add an example showing pattern binding when matching several values in a match arm (rust-lang/rust-by-example#1966) - Fix typo in linked list length calculation comment (rust-lang/rust-by-example#1968)
Update books ## rust-lang/book 6 commits in af415fc6c8a6823dfb4595074f27d5a3e9e2fe49..f660f341887c8bbcd6c24fbfdf5d2a262f523965 2025-10-28 01:41:51 UTC to 2025-10-24 15:39:51 UTC - Update to Rust 1.90 (rust-lang/book#4545) - Update to Rust 1.89 (rust-lang/book#4544) - Update to Rust 1.88 (rust-lang/book#4543) - Update to Rust 1.87 (rust-lang/book#4542) - Update to Rust 1.86 (rust-lang/book#4541) - Remove unused subheadings (rust-lang/book#4538) ## rust-lang/edition-guide 1 commits in e2ed891f00361efc26616d82590b1c85d7a8920e..5c621253d8f2a5a4adb64a6365905db67dffe3a2 2025-10-23 14:13:01 UTC to 2025-10-23 14:13:01 UTC - Add back the link for never_type_fallback_flowing_into_unsafe (rust-lang/edition-guide#378) ## rust-lang/reference 12 commits in 752eab01cebdd6a2d90b53087298844c251859a1..e122eefff3fef362eb7e0c08fb7ffbf5f9461905 2025-10-28 20:52:27 UTC to 2025-10-21 19:42:06 UTC - Remove custom redirect scripts (rust-lang/reference#2065) - Avoid specifying "last import wins" for MBEs (rust-lang/reference#2057) - Add caveats about mutable references in consts (rust-lang/reference#2058) - Move punctuation index to an appendix, and expand for other syntaxes (rust-lang/reference#2061) - Use American spelling for "labeled" (rust-lang/reference#2066) - Remove rules from names intro (rust-lang/reference#2060) - Minor adjustments to inner/outer attribute definitions (rust-lang/reference#2059) - Change underscore to be a keyword (rust-lang/reference#2049) - Update `proc_macro_attribute` to use the attribute template (rust-lang/reference#1889) - Update `proc_macro` to use the attribute template (rust-lang/reference#1887) - Update `macro_use` to use the attribute template (rust-lang/reference#1886) - Update `macro_export` to use the attribute template (rust-lang/reference#1885) ## rust-lang/rust-by-example 7 commits in 2c9b490d70e535cf166bf17feba59e594579843f..160e6bbca70b0c01aa4de88d19db7fc5ff8447c3 2025-11-03 12:26:45 UTC to 2025-10-22 13:19:06 UTC - Fix typos in flow_control/match/binding (rust-lang/rust-by-example#1971) - add clarification on overflowing_literals lint (rust-lang/rust-by-example#1970) - Update enum_use.md (rust-lang/rust-by-example#1960) - Remove weird extra spaces in code (rust-lang/rust-by-example#1962) - Include a link to The Rust Reference in flow_control/match/destructuring (rust-lang/rust-by-example#1963) - Add an example showing pattern binding when matching several values in a match arm (rust-lang/rust-by-example#1966) - Fix typo in linked list length calculation comment (rust-lang/rust-by-example#1968)
Update books ## rust-lang/book 6 commits in af415fc6c8a6823dfb4595074f27d5a3e9e2fe49..f660f341887c8bbcd6c24fbfdf5d2a262f523965 2025-10-28 01:41:51 UTC to 2025-10-24 15:39:51 UTC - Update to Rust 1.90 (rust-lang/book#4545) - Update to Rust 1.89 (rust-lang/book#4544) - Update to Rust 1.88 (rust-lang/book#4543) - Update to Rust 1.87 (rust-lang/book#4542) - Update to Rust 1.86 (rust-lang/book#4541) - Remove unused subheadings (rust-lang/book#4538) ## rust-lang/edition-guide 1 commits in e2ed891f00361efc26616d82590b1c85d7a8920e..5c621253d8f2a5a4adb64a6365905db67dffe3a2 2025-10-23 14:13:01 UTC to 2025-10-23 14:13:01 UTC - Add back the link for never_type_fallback_flowing_into_unsafe (rust-lang/edition-guide#378) ## rust-lang/reference 12 commits in 752eab01cebdd6a2d90b53087298844c251859a1..e122eefff3fef362eb7e0c08fb7ffbf5f9461905 2025-10-28 20:52:27 UTC to 2025-10-21 19:42:06 UTC - Remove custom redirect scripts (rust-lang/reference#2065) - Avoid specifying "last import wins" for MBEs (rust-lang/reference#2057) - Add caveats about mutable references in consts (rust-lang/reference#2058) - Move punctuation index to an appendix, and expand for other syntaxes (rust-lang/reference#2061) - Use American spelling for "labeled" (rust-lang/reference#2066) - Remove rules from names intro (rust-lang/reference#2060) - Minor adjustments to inner/outer attribute definitions (rust-lang/reference#2059) - Change underscore to be a keyword (rust-lang/reference#2049) - Update `proc_macro_attribute` to use the attribute template (rust-lang/reference#1889) - Update `proc_macro` to use the attribute template (rust-lang/reference#1887) - Update `macro_use` to use the attribute template (rust-lang/reference#1886) - Update `macro_export` to use the attribute template (rust-lang/reference#1885) ## rust-lang/rust-by-example 7 commits in 2c9b490d70e535cf166bf17feba59e594579843f..160e6bbca70b0c01aa4de88d19db7fc5ff8447c3 2025-11-03 12:26:45 UTC to 2025-10-22 13:19:06 UTC - Fix typos in flow_control/match/binding (rust-lang/rust-by-example#1971) - add clarification on overflowing_literals lint (rust-lang/rust-by-example#1970) - Update enum_use.md (rust-lang/rust-by-example#1960) - Remove weird extra spaces in code (rust-lang/rust-by-example#1962) - Include a link to The Rust Reference in flow_control/match/destructuring (rust-lang/rust-by-example#1963) - Add an example showing pattern binding when matching several values in a match arm (rust-lang/rust-by-example#1966) - Fix typo in linked list length calculation comment (rust-lang/rust-by-example#1968)
Rollup merge of #148448 - rustbot:docs-update, r=ehuss Update books ## rust-lang/book 6 commits in af415fc6c8a6823dfb4595074f27d5a3e9e2fe49..f660f341887c8bbcd6c24fbfdf5d2a262f523965 2025-10-28 01:41:51 UTC to 2025-10-24 15:39:51 UTC - Update to Rust 1.90 (rust-lang/book#4545) - Update to Rust 1.89 (rust-lang/book#4544) - Update to Rust 1.88 (rust-lang/book#4543) - Update to Rust 1.87 (rust-lang/book#4542) - Update to Rust 1.86 (rust-lang/book#4541) - Remove unused subheadings (rust-lang/book#4538) ## rust-lang/edition-guide 1 commits in e2ed891f00361efc26616d82590b1c85d7a8920e..5c621253d8f2a5a4adb64a6365905db67dffe3a2 2025-10-23 14:13:01 UTC to 2025-10-23 14:13:01 UTC - Add back the link for never_type_fallback_flowing_into_unsafe (rust-lang/edition-guide#378) ## rust-lang/reference 12 commits in 752eab01cebdd6a2d90b53087298844c251859a1..e122eefff3fef362eb7e0c08fb7ffbf5f9461905 2025-10-28 20:52:27 UTC to 2025-10-21 19:42:06 UTC - Remove custom redirect scripts (rust-lang/reference#2065) - Avoid specifying "last import wins" for MBEs (rust-lang/reference#2057) - Add caveats about mutable references in consts (rust-lang/reference#2058) - Move punctuation index to an appendix, and expand for other syntaxes (rust-lang/reference#2061) - Use American spelling for "labeled" (rust-lang/reference#2066) - Remove rules from names intro (rust-lang/reference#2060) - Minor adjustments to inner/outer attribute definitions (rust-lang/reference#2059) - Change underscore to be a keyword (rust-lang/reference#2049) - Update `proc_macro_attribute` to use the attribute template (rust-lang/reference#1889) - Update `proc_macro` to use the attribute template (rust-lang/reference#1887) - Update `macro_use` to use the attribute template (rust-lang/reference#1886) - Update `macro_export` to use the attribute template (rust-lang/reference#1885) ## rust-lang/rust-by-example 7 commits in 2c9b490d70e535cf166bf17feba59e594579843f..160e6bbca70b0c01aa4de88d19db7fc5ff8447c3 2025-11-03 12:26:45 UTC to 2025-10-22 13:19:06 UTC - Fix typos in flow_control/match/binding (rust-lang/rust-by-example#1971) - add clarification on overflowing_literals lint (rust-lang/rust-by-example#1970) - Update enum_use.md (rust-lang/rust-by-example#1960) - Remove weird extra spaces in code (rust-lang/rust-by-example#1962) - Include a link to The Rust Reference in flow_control/match/destructuring (rust-lang/rust-by-example#1963) - Add an example showing pattern binding when matching several values in a match arm (rust-lang/rust-by-example#1966) - Fix typo in linked list length calculation comment (rust-lang/rust-by-example#1968)
As it turns out, the story that "the final value of a constant cannot contain any mutable references" has some nuance to it. Let's describe these caveats.
Thanks to theemathas for noticing these and to RalfJ for filling in context.
cc @RalfJung @oli-obk @theemathas @tshepang @ehuss