# \[Pre-RFC\] Single function call \`unsafe\`

**URL:** <https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343>\
**Category:** language design\
**Created:** [February 4, 2025, 9:20pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343 "2025-02-04T21:20:14Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![tczajka](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tczajka/32/8923_2.png) [@tczajka](https://internals.rust-lang.org/u/tczajka)\
**Post date:** [February 4, 2025, 9:20pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/1 "2025-02-04T21:20:14Z")

</div>

## Motivation

The typical purpose of an `unsafe` block is to call a single `unsafe` function. There is no clean notation for this.

Wrapping the call in an `unsafe` block has the downside that it also makes the evaluation of parameters `unsafe`. So this is not a minimal `unsafe` block:

```rust
// SAFETY: foo is safe because ...
let x = unsafe { foo(expr1(), expr2()) };

```

What you typically really want is only for the single `foo` call to be `unsafe`, not `expr1` and `expr2`.

## Proposal

The `unsafe` keyword in front of a function call indicates the single function call is `unsafe`. Parameters are still evaluated safely.

```rust
// Only the `foo` call is unsafe.
let x = unsafe foo(expr1(), expr2());

```

It works with method calls and call chains:

```rust
let x = a
    .abc()
    .unsafe def()
    .xyz()
    .unsafe ghi();

```

This syntax is slightly ambiguous:

```rust
let x = unsafe foo()(); // which call is unsafe?

```

This can be resolved by precedence rules, i.e. only the first call is `unsafe`.

## Alternative postfix notation

To avoid parsing ambiguity postfix notation could be used:

```rust
// foo is unsafe
let x = foo.unsafe(expr1(), expr2());

```

This also works with method calls and call chains:

```rust
let x = a
    .abc()
    .def.unsafe()
    .xyz()
    .ghi.unsafe();

```

**Edit** : Made the prefix syntax the primary proposal.

---

<div class="post-metadata">

**Author:** ![ia0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ia0/32/1396_2.png) [@ia0](https://internals.rust-lang.org/u/ia0)\
**Post date:** [February 4, 2025, 9:32pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/2 "2025-02-04T21:32:30Z")

</div>

What about having the arguments be variables? The advantage is that it works today. The disadvantage is that you have to find good names. So far I've done this and I didn't feel the need for something else.

```rust
let arg1 = expr1();
let arg2 = expr2();
// SAFETY: foo is safe on arg1 and arg2
let x = unsafe { foo(arg1, arg2) };

```

---

<div class="post-metadata">

**Author:** ![tczajka](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tczajka/32/8923_2.png) [@tczajka](https://internals.rust-lang.org/u/tczajka)\
**Post date:** [February 4, 2025, 9:37pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/3 "2025-02-04T21:37:25Z")

</div>

> [@ia0](#):
>
> ```rust
> let arg1 = expr1();
> let arg2 = expr2();
> // SAFETY: foo is safe on arg1 and arg2
> let x = unsafe { foo(arg1, arg2) };
> 
> ```

I've been using this workaround but it's annoying. In practice many other people don't bother because it's so verbose. One is forced to choose between conciseness and safety. This also doesn't work well with call chains. I feel that the addition of `.unsafe` is small enough to be worth it because it targets a common pattern.

---

<div class="post-metadata">

**Author:** ![ia0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ia0/32/1396_2.png) [@ia0](https://internals.rust-lang.org/u/ia0)\
**Post date:** [February 4, 2025, 9:41pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/4 "2025-02-04T21:41:52Z")

</div>

> [@tczajka](#):
>
> I've been using this workaround but it's annoying.

Yes, I agree it can feel annoying. But the question is whether the pros outweigh the cons. For example, by explicitly naming the arguments, one can talk about them in the safety comment, which is actually convenient.

> [@tczajka](#):
>
> This also doesn't work well with call chains

I would argue that if you have call chains, the safety comments might get messy. It's probably better to have at most one unsafe block (or call) per statement, such that safety comments apply to a given statement.

---

<div class="post-metadata">

**Author:** ![SkiFire13](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skifire13/32/7579_2.png) [@SkiFire13](https://internals.rust-lang.org/u/SkiFire13)\
**Post date:** [February 4, 2025, 9:44pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/5 "2025-02-04T21:44:34Z")

</div>

> [@tczajka](#):
>
> However this syntax is slightly ambiguous:
> 
> ```rust
> let x = unsafe foo()(); // which call is unsafe?
> 
> ```

Couldn't this just be defined to be the innermost one, i.e. the `unsafe _ ()` operator has higher precedence than the `_ ()` operator?

Another option would be disallow it, since the two meanings can be expressed as `(unsafe foo())()` and `unsafe (foo())()`.

Honestly to me this syntax looks much better than the proposed one.

> [@ia0](#):
>
> What about having the arguments be variables?

Doing this with `let` has different semantics wrt temporaries, you'll have to use `match expr1 { arg1 => ... }` instead if you want to be fully generic, which is however much more verbose.

---

<div class="post-metadata">

**Author:** ![ia0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ia0/32/1396_2.png) [@ia0](https://internals.rust-lang.org/u/ia0)\
**Post date:** [February 4, 2025, 9:51pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/6 "2025-02-04T21:51:08Z")

</div>

> [@SkiFire13](#):
>
> Doing this with `let` has different semantics wrt temporaries

Good point.

Also, don't get me wrong, I think having `unsafe` on unsafe operations directly, the same as for `unsafe impl`, is definitely good. I'm just not a big fan of additional syntax. From the suggested one, the postfix is obviously better because it's on the application syntax node. It could also be `foo unsafe(args, ...)` without the dot. The same would be needed for unsafe operations like `*unsafe ptr`, etc. But I still believe that the problematic of safety comments will be present. If you have multiple unsafe operations in the same statement, the documentation might get messy.

---

<div class="post-metadata">

**Author:** ![tczajka](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tczajka/32/8923_2.png) [@tczajka](https://internals.rust-lang.org/u/tczajka)\
**Post date:** [February 4, 2025, 9:55pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/7 "2025-02-04T21:55:26Z")

</div>

> [@ia0](#):
>
> But I still believe that the problematic of safety comments will be present. If you have multiple unsafe operations in the same statement, the documentation might get messy.

The `unsafe` block already allows you to have multiple `unsafe` operations in a single statement. If you want to do that, the proposal makes it easier to mark both explicitly, which can only help documenting safety.

---

<div class="post-metadata">

**Author:** ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)\
**Post date:** [February 4, 2025, 9:57pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/8 "2025-02-04T21:57:42Z")

</div>

I personally prefer the prefixed `unsafe foo`. It’s almost as if the `unsafe` prefix became part of the name of the function, and reminds me of common practice in Haskell, where unsafe API is done (with no language support) by `unsafe_` prefixing the function names.\[1\]

Regarding ambiguity, I don’t think `foo()()` is common enough to be of concern. To avoid confusion, one can simply enforce restrictions against it. Perhaps comparable to how `x.foo()` can’t be a call of an `FnOnce()`-typed _field_ of `x` called `foo`, requiring `(x.foo)()` instead.

Conservatively, one could disallow `unsafe foo()()`; then `(unsafe foo())()` would be a valid alternative for explicitly calling the inner function unsafely, and the outer one needs not be supported at all; requiring the workaround of `let f = foo(); unsafe f();` is fine for now.\[2\]

For chains, I see no problems at all, either. What’s wrong with the following syntax?

```rust
let x = a
    .abc()
    .unsafe def()
    .xyz()
    .unsafe ghi();

```

* * *

Regarding `SAFETY` comments, IMO that’s a separate/orthogonal concern – it’s also a question of style. `SAFETY` comments are underused, anyway; `unsafe` blocks often simultaneously too large and too small. Too small to contain a whole section of code where “all enforcement of invariants is contained within them” and too large to keep the effort of “find all the operations with preconditions” easily enough in review.

* * *

1. I say “`unsafe_`” for the Rust audience, but there’s not _really_ an underscore because `lowerCamelCase` is common practice 

2. Allowing `unsafe (foo())()` is confusing IMO, because it’s too close to `unsafe { foo() }()` but with the opposite meaning! On the other hand, it’s of course still useful to explore this question a bit – _eventually_ – in the context of discussing future possibilities.

---

<div class="post-metadata">

**Author:** ![ia0](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ia0/32/1396_2.png) [@ia0](https://internals.rust-lang.org/u/ia0)\
**Post date:** [February 4, 2025, 10:21pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/9 "2025-02-04T22:21:00Z")

</div>

> [@steffahn](#):
>
> Regarding `SAFETY` comments, IMO that’s a separate/orthogonal concern – it’s also a question of style.

Yes, it's partly orthogonal. The proposal is to have the `unsafe` keyword on the syntax node that needs it, rather than being a scope over a syntax tree. It is already possible to follow this style today by moving the children of a syntax node outside its syntax tree (e.g. using `let` before and possibly `drop` after). This style is useful for safety comments, by having statements with exactly one operation, that operation being unsafe, and that statement being commented. But it's true that you don't need this style because of safety comments. So it goes only one way, hence partly orthogonal. I still believe that to motivate this change, the impact (ideally positive) on safety comments needs to be considered.

> [@steffahn](#):
>
> `SAFETY` comments are underused

True.

> [@steffahn](#):
>
> Too small to contain a whole section of code where “all enforcement of invariants is contained within them”

I don't think unsafe blocks can be too small. That's actually the point of this proposal, to have them as small as possible, targeting exactly one syntax node.

The "enforcement of invariants" part is just the scope of unsafe. Unsafe blocks should not be used to define the scope of unsafe. It is implicitly defined by the set of public functions that cover the unsafe block.

---

<div class="post-metadata">

**Author:** ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)\
**Post date:** [February 4, 2025, 10:47pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/10 "2025-02-04T22:47:39Z")

</div>

> [@ia0](#):
>
> It is already possible to follow this style today by moving the children of a syntax node outside its syntax tree (e.g. using `let` before and possibly `drop` after).

Do note that this can become pretty bad with closure callbacks. If you have a `foo(bar, |x| { …code… })` kind of API, it’s hard on readability to move the closure out of the call expression, and if the closure involves generic lifetimes, it might be impossible without _significant_ additional effort to define the lifetimes relationships in a different manner.

Even without lifetimes, it’s just _natural_ that users wouldn’t care to change their code like this; much realistically, you _will_ end up with users that wrap the whole call in `unsafe` and call it a day. Working around this with … creative … API design is also a hassle, and somewhat unsatisfying. (E.g. [in this discussion](https://github.com/PyO3/pyo3/issues/3640#issuecomment-1851817489) such an issue came up; and a possible workaround I’ve mentioned there would be an API in the style of `foo(unsafe { foo_safety_marker() }, bar, |x| { …code… })` – but IMO `unsafe foo(bar, |x| { …code… })` would be _much, much_ nicer.)

---

<div class="post-metadata">

**Author:** ![FZs](https://avatars.discourse-cdn.com/v4/letter/f/85f322/32.png) [@FZs](https://internals.rust-lang.org/u/FZs)\
**Post date:** [February 4, 2025, 10:58pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/11 "2025-02-04T22:58:38Z")

</div>

> [@tczajka](#):
>
> Wrapping the call in an `unsafe` block has the downside that it also makes the evaluation of parameters `unsafe`. So this is not a minimal `unsafe` block:
> 
> ```rust
> // SAFETY: foo is safe because ...
> let x = unsafe { foo(expr1(), expr2()) };
> 
> ```

This works OK today with `clippy::multiple_unsafe_ops_per_block` enabled. Although I see that not everyone uses that lint and having this feature would encourage minimal unsafe even by those who don't use the lint.

> [@steffahn](#):
>
> For chains, I see no problems at all, either. What’s wrong with the following syntax?
> 
> ```rust
> let x = a
> .abc()
> .unsafe def()
> .xyz()
> .unsafe ghi();
> 
> ```

This is a great selling point IMO.

---

<div class="post-metadata">

**Author:** ![tczajka](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tczajka/32/8923_2.png) [@tczajka](https://internals.rust-lang.org/u/tczajka)\
**Post date:** [February 4, 2025, 11:09pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/12 "2025-02-04T23:09:27Z")

</div>

I have edited the proposal to make the prefix syntax the primary proposal because I also feel it looks much nicer than the postfix `.unsafe` notation, and I've been convinced the syntactic ambiguity can easily be resolved by precedence rules.

The postfix notation is still there as an alternative.

---

<div class="post-metadata">

**Author:** ![tczajka](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tczajka/32/8923_2.png) [@tczajka](https://internals.rust-lang.org/u/tczajka)\
**Post date:** [February 4, 2025, 11:23pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/13 "2025-02-04T23:23:44Z")

</div>

> [@ia0](#):
>
> For example, by explicitly naming the arguments, one can talk about them in the safety comment, which is actually convenient.

You can talk about more complicated expressions in a safety comment:

```rust
// SAFETY: `n + 1` is in range because `n + 1 < ...`
let x = 2 * slice.unsafe get_unchecked(n + 1);

```

vs

```rust
let index = n + 1;
// SAFETY: `index` is in range because `index = n + 1 < ...`.
let x = 2 * unsafe { slice.get_unchecked(index) };

```

---

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [February 4, 2025, 11:40pm UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/14 "2025-02-04T23:40:46Z")

</div>

To me this is unnecessary, and even a move in a wrong direction.

It's very rare that a single call by itself encapsulates all of the unsafety. Unsafety usually depends on function arguments, on various preconditions, and may also need keeping an eye on destructors and panics. I would prefer `unsafe` blocks to be _larger_ to cover all of these concerns together, because the entire context must be checked for safety.

If you're calling something like `get_unchecked(index)`, computation of the `index` is the actual unsafe thing. I'd prefer index calculation to be added to the `unsafe` block, not the block made even slimmer and more incomplete.

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [February 5, 2025, 12:11am UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/15 "2025-02-05T00:11:19Z")

</div>

> [@SkiFire13](#):
>
> Another option would be disallow it, since the two meanings can be expressed as `(unsafe foo())()` and `unsafe (foo())()`.

Actually, I think the latter could lead to confusion. Unsafe function calls shouldn't support arbitrary expressions, only an identifier followed by parentheses:

```rs
// Allowed:
unsafe foo(..)
foo.unsafe bar(..)

// NOT allowed:
unsafe (foo())()
unsafe { foo }() // parses as a regular unsafe {} block instead
unsafe foo.bar()
unsafe foo?()
// etc.

```

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [February 5, 2025, 12:27am UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/16 "2025-02-05T00:27:55Z")

</div>

> [@kornel](#):
>
> I would prefer `unsafe` blocks to be _larger_ to cover all of these concerns together, because the entire context must be checked for safety.

Agreed. What I want help with is making sure I satisfied all the preconditions, not just knowing which things _have_ preconditions.

I'll re-use this previous post about it:

> [@Pre-RFC: Unsafe reasons](https://internals.rust-lang.org/t/pre-rfc-unsafe-reasons/22093/19):
>
> TBH, I think needing them in the middle of the call like that will defacto result in people being unwilling to actually list out the reasons in nice separable ways, because it'll just be so ugly to see them all through the middle of the logic. I do like the direction of having to meet all the requirements -- not just the ones you remembered -- but think it should work differently so while you still need to cover them, you don't need to repeat them endlessly. For example, I think of code like …

(If we can write proofs of predicates, then all these individual `unsafe`s become unnecessary.)

---

<div class="post-metadata">

**Author:** ![toc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/toc/32/6692_2.png) [@toc](https://internals.rust-lang.org/u/toc)\
**Post date:** [February 5, 2025, 1:17am UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/17 "2025-02-05T01:17:23Z")

</div>

> [@kornel](#):
>
> I would prefer `unsafe` blocks to be _larger_ to cover all of these concerns together, because the entire context must be checked for safety.

There are multiple competing concerns being served by `unsafe`. The one you mention is potentially well served by just regular blocks and comments. I don't know how this will shake out, but I would like to be able to write code along the lines of:

```rust
// SAFETY: we're going to get all the right inputs for `super_unsafe`
{
    // We need to add 2 for reasons.
    let a = safe_thing() + 2;
    let b = also_safe(*p);
    let c = unsafe super_unsafe(a, b);
    more_safe_things(c);
}

```

To me a large `unsafe` block is making that harder to review, but structuring it and documenting it nicely certainly does help.

---

<div class="post-metadata">

**Author:** ![tczajka](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tczajka/32/8923_2.png) [@tczajka](https://internals.rust-lang.org/u/tczajka)\
**Post date:** [February 5, 2025, 1:21am UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/18 "2025-02-05T01:21:21Z")

</div>

> [@kornel](#):
>
> It's very rare that a single call by itself encapsulates all of the unsafety.

It's also common that soundness requirements extend beyond a single function, so clearly you can't expect that one `unsafe` block contains all the code that interacts with these requirements because blocks are restricted to a single function. "Unsafe blocks" and "safety abstraction boundary" are two different concepts by necessity. Often the whole module or the whole crate is the safety abstraction boundary.

> [@kornel](#):
>
> I would prefer `unsafe` blocks to be _larger_

Your preferred style may be valid, but surely the "minimal unsafe" style is also valid and common, so I'm trying to cater to it. It's definitely what I do in my code.

---

<div class="post-metadata">

**Author:** ![rkuhn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/rkuhn/32/6659_2.png) [@rkuhn](https://internals.rust-lang.org/u/rkuhn)\
**Post date:** [February 5, 2025, 6:43am UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/19 "2025-02-05T06:43:19Z")

</div>

`unsafe` does not have the purpose of providing a nice syntax, quite the opposite: it is supposed to stick out, be an eyesore and inconvenient. Encountering `unsafe` during a review is supposed to stop you right there and go “what the heck is going on here?” The result hopefully is that after close scrutiny the response is “ah, right, okay.”

So, as a bystander on the peanut gallery this whole discussion feels like it started from the wrong premise.

---

<div class="post-metadata">

**Author:** ![FZs](https://avatars.discourse-cdn.com/v4/letter/f/85f322/32.png) [@FZs](https://internals.rust-lang.org/u/FZs)\
**Post date:** [February 5, 2025, 7:59am UTC](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343/20 "2025-02-05T07:59:28Z")

</div>

> [@kornel](#):
>
> It's very rare that a single call by itself encapsulates all of the unsafety.

> [@toc](#):
>
> There are multiple competing concerns being served by `unsafe`

> [@tczajka](#):
>
> It's also common that soundness requirements extend beyond a single function

Before we go much further into this discussion again, let me summarize the way I see this:

There seems to be two\[1\], quite contradictory approaches to `unsafe` blocks. Both of them has valid arguments for it, and there seems to be no consensus on which is the "official way".

1. Make `unsafe` blocks as small as possible

2. Make `unsafe` blocks encompass a set of operations that are key to upholding the invariants

3. It's All Unsafe Anyway™

* * *

1. and one more, but that one's not great

[Next page](https://internals.rust-lang.org/t/pre-rfc-single-function-call-unsafe/22343.md?page=2)
