openlang

unsafe belongs on the extern block, not the call site

Rust 2024 requires unsafe extern "C" { ... } instead of a bare extern block, and it's the most honest small syntax change the language has made in years. Declaring a foreign function was always an unchecked claim about its signature. Get the argument types wrong and every call is undefined behavior, however carefully the call site was reviewed. The old syntax put unsafe only at the call sites, which pointed reviewers at the wrong lines.

Inside the block an item can now be marked safe fn when the foreign function really has no preconditions, and callers then need no unsafe at all. That moves the whole burden onto one declaration, which is exactly where the review belongs.

The same edition makes unsafe_op_in_unsafe_fn warn by default, so the body of an unsafe fn no longer counts as one big unsafe block. Each operation needs its own unsafe { } and, by convention, a SAFETY comment saying why it holds.

The next step should be a required justification on every safe fn in a foreign block, checked the way Clippy's undocumented_unsafe_blocks can already demand a SAFETY comment.

后续讨论 5

顺着彼此的依据读下去
  1. 固定链接

    I follow Carbon more closely than Rust, and it is aiming at the other end of this. The plan is for Carbon code to import C++ headers directly, with no hand-written foreign declarations at all, so a mismatched signature has nowhere to be typed in. Whether the safety story holds up is still open. The project calls itself experimental and its memory safety design isn't finished, so anything said about Carbon's interop soundness for now is intent rather than behavior.

  2. 固定链接

    The signature is half of it. The other half is unwinding. A panic that tries to leave an extern "C" Rust function now aborts the process, and a C++ exception crossing into Rust is only defined through the "C-unwind" ABI. So the declaration also has to say whether anything can unwind through it. With an abort no destructor runs at all, so a handle that needed an orderly close never gets one.

  3. 固定链接

    This has a near twin in Swift's Sendable checking, which is where I usually run into it. C headers say nothing about which thread may touch what, so a wrapper around a C handle usually ends up marked @unchecked Sendable, and the compiler takes the author's word for it, much like a safe fn in a foreign block. I'd keep those rare and easy to grep, each with a comment naming the lock or queue that makes it true. And @preconcurrency import quietly downgrades a module's Sendable diagnostics, which is fine during migration and easy to forget afterwards.

  4. 固定链接

    Not quite the same sentence. @unchecked Sendable vouches for a type everywhere it travels, while a safe fn vouches for one foreign function's preconditions. Both should be easy to grep, and Rust at least forces the word unsafe onto the block that holds the vouching, so one search finds every claim.

  5. 固定链接

    Read the diff cargo fix --edition gives you on an FFI crate before trusting it. Adding unsafe to every extern block is expected, but it also wraps every unsafe fn body whole.

    pub unsafe fn len_of(s: *const c_char) -> usize { unsafe {
        let n = strlen(s);
        n + abs(0) as usize
    }}

    That compiles under 2024 and keeps the lint quiet, and it is exactly what the lint exists to retire. No SAFETY comment, nothing to review. Split those by hand into one block per operation.

    safe fn is left to you as well. A function that takes and returns an integer can become safe fn. One that takes a pointer stays unsafe until you can write an honest justification for it.

同一个问题的其他角度

  1. foldl composes fine and leaks anyway

    foldl (+) 0 over a long list is the smallest space leak I know, and it's a composition problem more than an arithmetic one. E…

    回复 6最近活动 haskell
  2. Empty reductions return the identity

    Reduce over an empty vector. APL answers. Each primitive carries its identity. Zero for plus. One for times and for and. ⌈/⍬…

    回复 3最近活动 apl
  3. Returning from a Common Lisp handler means declining

    Neat abbreviation. I'd still like to see what it's agreeing to. The wrapper macro installs a handler around a parser, and the…

    回复 4最近活动 common-lisp