Repository navigation
Tracking Issue for "unsafe blocks in unsafe fn" (RFC #2585) #71668
Copy link
Copy link
Open
Labels
B-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.Blocker: Approved by a merged RFC but not yet implemented.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCS-tracking-impl-incompleteStatus: The implementation is incomplete.Status: The implementation is incomplete.T-langRelevant to the language teamRelevant to the language teamdisposition-mergeThis issue / PR is in PFCP or FCP with a disposition to merge it.This issue / PR is in PFCP or FCP with a disposition to merge it.finished-final-comment-periodThe final comment period is finished for this PR / Issue.The final comment period is finished for this PR / Issue.
Description
This is a tracking issue for the RFC "unsafe blocks in unsafe fn" (rust-lang/rfcs#2585).
The lint unsafe_block_in_unsafe_fn is stable, but the RFC proposes some further things we might want to do.
About tracking issues
Tracking issues are used to record the overall progress of implementation.
They are also uses as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.
Steps
- Introduce an opt-in lint that, when enabled, causes unsafe blocks in unsafe functions to be considered required, and warns if they are absent when an unsafe operation is performed.
- Stabilization PR in Rust 1.52.0 (Stabilize
unsafe_op_in_unsafe_fnlint #79208) - Include a suggestion with the lint that can insert required unsafe blocks. This could be as simple as adding a block across the entire function, though more granular insertion is probably better. (Add MVP suggestion for
unsafe_op_in_unsafe_fn#112017) - Adjust documentation to describe the (somewhat unusual) effect of the lint, and to describe the possibility that the lint will be enabled default (see instructions on rustc-dev-guide)
- Write a blog post describing the change, perhaps?
Unresolved Questions
- What is the timeline for adding the lint, and cranking up its default level?
- Should the default level depend on the edition?
- Should we ever make this deny-by-default or even a hard error, in a future edition?
- Should we require
cargo fixto be able to do something about this warning before making it even warn-by-default? And how precise should it be?
Implementation history
- Implemented in Implement RFC 2585: unsafe blocks in unsafe fn #71862
- Stabilized in Stabilize
unsafe_op_in_unsafe_fnlint #79208 - Fixable suggestion added in Add MVP suggestion for
unsafe_op_in_unsafe_fn#112017 - Made warn-by-default for 2024 edition in Change
unsafe_op_in_unsafe_fnto bewarn-by-default from edition 2024 #112038
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
B-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.Blocker: Approved by a merged RFC but not yet implemented.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCS-tracking-impl-incompleteStatus: The implementation is incomplete.Status: The implementation is incomplete.T-langRelevant to the language teamRelevant to the language teamdisposition-mergeThis issue / PR is in PFCP or FCP with a disposition to merge it.This issue / PR is in PFCP or FCP with a disposition to merge it.finished-final-comment-periodThe final comment period is finished for this PR / Issue.The final comment period is finished for this PR / Issue.
Type
Projects
- StatusShow more project fieldsNeeds help: Impl
Activity
[-]Tracking Issue for XXX[/-][+]Tracking Issue for "unsafe blocks in unsafe fn" (RFC #2585)[/+]nikomatsakis commentedon Apr 29, 2020
cc @RalfJung
nikomatsakis commentedon Apr 30, 2020
A relevant note towards the implementation:
LeSeulArtichaut commentedon May 3, 2020
I'll try to work on the implementation!
RalfJung commentedon May 27, 2020
If I understand https://boats.gitlab.io/blog/post/ringbahn/ correctly, that API would also benefit greatly from not treating the body of an
unsafe fnasunsafe {}: there is a trait with anunsafe fn, and@withoutboats did I understand this correctly?
withoutboats commentedon May 27, 2020
@RalfJung I don't think its a particularly new situation. Basically, the guarantee that you are getting from it being unsafe are the guarantees you need to be able to call these unsafe methods in the SubmissionQueueEvent that is passed as an argument.
In other words, an
Eventimplementation is going to use probably 1 unsafe operation (calling one of these methods), which it can prove is correct based on the invariant that trait requires the caller to maintain. This isn't really different from any other unsafe code.What I'm trying to say with the blog post is that the Event trait hasn't introduced a new invariant for you to maintain, its introduced a new invariant that I maintain so its easier for you to call that unsafe method you need to call to get any work done.
This is just how unsafe trait methods work in general, so its nothing new. But I agree that unsafe trait methods (where the invariant contract is specified separately from the implementer) are a compelling example of where unsafe blocks in unsafe fns seem beneficial.
RalfJung commentedon May 27, 2020
Is there a realistic safe implementation of that method that ignores the extra invariant provided by the caller, or is the expectation that most/all implementations will have to do something
unsafejustified by that invariant?withoutboats commentedon May 27, 2020
Yea I believe some kinds of events should be safe to prepare because they only send
Copydata into the kernel and so they don't need to worry about any kind of memory safety invariant.Rollup merge of rust-lang#71862 - LeSeulArtichaut:unsafe-block-in-uns…
Rollup merge of rust-lang#71862 - LeSeulArtichaut:unsafe-block-in-uns…
158 remaining items
horace-walsh commentedon Feb 12, 2026
This is achieving the exact opposite of the intended effect, it's making the usage of
unsaferiskier by making the "correct" usage pattern more verbose.Beforehand using
unsafe fnautomatically propagated theunsafetag up the call chain until a "willing" frame wrapped the call inunsafe { ... }. It was a clever trick to use convenience for propagation.With this change there is no propagation. From the compiler output's perspective there is literally no difference between
and
which means there is nothing to force the programmer to add the
unsafe fntags. Why would they, they have to add the op unsafe either way.If the goal is to avoid "accidental" unsafe calls in unsafe functions (...as if that was a real concern...) then personally I would approach it from a different angle. For example force the usage of
unsafeboth at the op and the fn, unless a new tag e.g.unsafe_trusted { .. }is used for the op, marking the wrap. Yes, it's verbose. Yes, it's inconvenient. But at least it's actually safe as opposed to #71668, which is both inconvenient and less safe.RalfJung commentedon Feb 12, 2026
The reasoning for why we think this lint is a step in the right direction is spelled out in the RFC. The typical systematic way of dealing with unsafe code is entirely undermined without this lint.
That already wasn't the case before, one could have added the unsafe block instead.
We've also already established project consensus for this. It's not really up for debate any more.
horace-walsh commentedon Feb 12, 2026
Indeed. However, previously the most common usage pattern was
which was safe. This change essentially removes this pattern, which means programmers have to pick between two choices, and most of then will opt for
I would even argue that newcomers will not understand the difference between the two choices. It will just be a compiler error that they have to "make go away".
Essentially this change will make programmers switch from a previously mostly safe tradeoff to an unsafe default.
I also understand that. However actual engineers will encounter these issues through the lint, not by browsing RFCs. Like I just did.
Thomasdezeeuw commentedon Feb 12, 2026
I would note that two examples mean two different things. The first says that function
somethingis unsafe and the caller needs to uphold some kind of constraint. The second says thatsomethingis safe and that the function itself is upholding the required constraints to callwhatever.In my opinion what you want is the following (which I think the RFC is suggesting, but perhaps the lint doesn't make clear enough).
RalfJung commentedon Feb 12, 2026
Actual engineers were confused by the old setup and misunderstood what
unsafeeven does and means. From the feedback we got, most people consider this change an improvement. In particular most people understand very well what it means to mark a function as safe vs unsafe. No system is perfect, obviously, and documentation can always be improved.https://doc.rust-lang.org/nightly/std/keyword.unsafe.html is a good starting point.
horace-walsh commentedon Feb 12, 2026
Yes, but not from a compiler feedback perspective.
Previously:
unsafe fn { op }: mostly opted for default, unsafe propagatesfn { unsafe op }: inline unsafe blocks are slightly less convenient, therefore force the programmer to think about whether it's actually safe to call opunsafe fn { unsafe op }: almost noone used thisNow:
fn { unsafe op }: what most people will opt for when writing new code.unsafe fn { unsafe op }: almost noone will use this for new code. Only for the lint this change introduced in existing code. Maybe not even that.No, that's clear enough, as should be to anyone who bothers to look up the docs. My point is exactly that this pattern (which should be the "correct" one) is not enforced and unclear why it would be needed just based on the compiler feedback. It is also inconvenient, therefore people are less likely to use it.
clarfonthey commentedon Feb 12, 2026
Side note, what is this issue tracking, currently? This lint is already enabled by default on the 2024 edition and it's unclear what else is left to do.
RalfJung commentedon Feb 12, 2026
Apparently
Not sure if any of those are actually still planned.
clarfonthey commentedon Feb 12, 2026
So:
?
(The post announcing the edition doesn't go into detail on this specifically, but it would be assumed that this is enough to qualify as "announcing" it.)
9thyear2 commentedon May 7, 2026
i tend to agree, i just came out of learning rust and i ended up asking myself when should i use an:
over
as there is no constraint to stop me using the second pattern over the first (i mean i defined one for myself (a personal rule (anytime i use raw pointers, it needs to be in an unsafe fn)), but to my knowledge that's not upheld by the compiler
your right and people don't want to admit it, people will begin to prefer the second pattern over the first one (not just devs, but those who are just getting started with rust too (who will just want to make the warnings and error disappear))
whats to stop people from defining safe functions that use a bunch of unsafe blocks (and making a "safe" function that's actually turns out to be unsafe)? while that was possible before. Requiring unsafe blocks in unsafe functions will make people prefer to just do that, as there would be almost no distinction when writing the code between an unsafe function and a regular function, as both would require unsafe blocks
what is needed for this to make sense is some forced use of unsafe functions, but if that doesn't accompany this. than this alone doesn't make sense
people are just like lightning... they take the path of least resistance. In this case that would be not typing the extra 7 characters
unsaferight beforefnfrom a human psychology standpoint, combined with rusts goals... this doesn't make any sense alone
SimonSapin commentedon May 8, 2026
unsafeis all about things the compiler cannot entirely check on its own. That’s why it’s called unsafe. I’m sorry but when usingunsafeat all there is no substitute for learning the meaning of these two patterns and using them accordingly. Namely: anunsafe fnis unsafe to call: the caller is responsible for maintaining some invariant (which should be documented).fnis safe to call with any parameter it accepts, and if anunsafe {…}block is used the function body itself is responsible to uphold the relevant invariants (ideally also with a comment that explains how).9thyear2 commentedon May 8, 2026
i wasn't referencing the compiler checking for safety, but what is being called within the code (eg. not allowing the use of raw pointers outside a specific pattern as i described in my personal rule)
the problem i was pointing out... is previously there were 2 ways of writing unsafe code:
and
this would cause a dev to stop and think what is more appropriate because of the differences between how the code could be wrote
but now you did the meme ("they're the same picture"):
regardless whether its safe so not, that stop and pause is now removed, as there is no difference in the code between writing a safe or unsafe function
you might define one in the docs, but the docs aren't right in your face as your writing the code. and most tutorials that people watch use an older version of rust where this wasn't a thing
so as stated before... when presented with this the majority of people will act just like lightning, and take the path of least resistance (or use this all the time):
and that personal rule i mentioned... was for me to introduce a differentiator back between the 2 styles (so i have a stop and pause)
you should look at doing the same
ojeda commentedon May 8, 2026
Just to clarify: there are safe functions that use unsafe code inside them, and that is fine and expected -- your raw pointer personal rule sounds like it will make you mark functions as unsafe that should have been safe.
One could argue that
safe fnshould have been introduced as a way to opt-out, but the current way is simpler and more regular, in the sense that each of the positions of the keyword does something orthogonal to the other.I would suggest using Zulip to discuss this though, since there are probably quite a lot of subscribers in this issue and any change around this would require a lot of discussion.
Thanks!
briansmith commentedon May 8, 2026
It does matter. "
unsafe fn" means that users of your function need to uphold safety invariants that the Rust language doesn't enforce, when calling your function.fnwithoutunsafemeans that there is nothing special that the caller needs to do. This is why every combination, all of the following, is needed:I guess writing the documentation that clarifies this, exactly to help people who find it confusing, is the main reason why this issue is still open (bullet point "Adjust documentation to describe the (somewhat unusual) effect of the lint". Not really any reason to stay subscribed to this for anybody not interested in that.
9thyear2 commentedon May 8, 2026
why do you guys insist on misinterpreting what i was saying on purpose
wasn't referring what its purpose was, it was to simplify it so i didn't have to put 2 code examples with a 7 character difference
it almost feels like what im saying is getting taken out of context to reaffirm this decision
i was trying to be a little more professional and not use an actual meme, but since you insist on that here you go then:
all i was saying is a subtle difference in how you write the code will cause someone to pause and think about how best to proceed (yes my personal rule isn't perfect, but neither is expecting everyone to have the rust docs pulled up on a second screen to make sure the they are using each pattern correctly (or memorize the docs))
what i was saying was introduce a subtle difference between the 2 styles again (my personal rule was an example (hence the word "personal")), but at least is causes me to take a pause and think what i am doing