openlang

unsafe는 호출 지점이 아니라 extern 블록에 붙는다

Rust 2024는 맨 extern 블록 대신 unsafe extern "C" { ... }를 요구한다. 최근 몇 년 사이 이 언어가 한 작은 문법 변경 가운데 가장 정직하다. 외부 함수를 선언하는 것은 언제나 시그니처에 대한 검증되지 않은 주장이었다. 인자 타입을 틀리면 호출 지점을 아무리 꼼꼼히 리뷰했어도 모든 호출이 정의되지 않은 동작이다. 예전 문법은 unsafe를 호출 지점에만 두었고, 리뷰어의 눈을 엉뚱한 줄로 돌렸다.

이제 블록 안에서 외부 함수에 정말 전제 조건이 없으면 항목을 safe fn으로 표시할 수 있고, 그러면 호출자는 unsafe가 전혀 필요 없다. 부담 전체가 선언 하나로 옮겨 가는데, 리뷰가 있어야 할 곳이 정확히 거기다.

같은 에디션에서 unsafe_op_in_unsafe_fn이 기본 경고가 되어 unsafe fn 본문은 더 이상 하나의 커다란 unsafe 블록으로 취급되지 않는다. 연산마다 따로 unsafe { }가 필요하고, 관례상 왜 성립하는지 적은 SAFETY 주석이 붙는다.

다음 단계는 외부 블록 안의 모든 safe fn에 근거를 적도록 강제하는 것이어야 한다. 린트로 충분히 할 수 있다.

이어지는 대화 4

서로의 근거를 따라
  1. 고유 링크

    Carbon은 이 문제의 반대쪽 끝을 겨냥하고 있습니다. Carbon 코드가 C++ 헤더를 직접 가져오고 손으로 쓰는 외부 선언은 아예 없게 하겠다는 계획이라, 어긋난 시그니처를 적어 넣을 자리가 없습니다. 안전성 이야기가 버텨 줄지는 아직 열려 있습니다. 프로젝트 스스로 실험적이라고 밝히고 있고 메모리 안전성 설계도 끝나지 않았으니, 지금 Carbon의 상호 운용 건전성에 대해 하는 말은 동작이 아니라 의도로 받아들여야 합니다.

  2. 고유 링크

    시그니처는 절반이다. 나머지 절반은 되감기다. extern "C" Rust 함수를 빠져나가려는 패닉은 이제 프로세스를 중단시키고, Rust로 넘어오는 C++ 예외는 "C-unwind" ABI를 통해서만 정의된다. 그러니 선언은 그것을 통해 무엇이든 되감길 수 있는지도 밝혀야 한다. 그때 핸들을 소유한 완성된 객체는 무엇인가? 중단이라면 소멸자는 하나도 실행되지 않는다.

  3. 고유 링크

    Swift의 Sendable 검사에 거의 쌍둥이 같은 문제가 있어요. C 헤더는 어느 스레드가 무엇을 만져도 되는지 아무것도 말해 주지 않아서, C 핸들을 감싼 타입은 대개 @unchecked Sendable로 표시되고 컴파일러는 작성자 말을 그대로 믿어요. 외부 블록 안의 safe fn과 거의 같아요. 저라면 이런 표시는 드물게, grep으로 찾기 쉽게 두고, 그걸 참으로 만드는 락이나 큐를 주석으로 적겠어요. 그리고 @preconcurrency import는 모듈의 Sendable 진단을 조용히 낮춰요. 마이그레이션 중엔 괜찮지만 끝나고 나면 잊기 쉬워요.

  4. 고유 링크

    완전히 같은 문장은 아니다. @unchecked Sendable은 타입이 가는 모든 곳에 대해 보증하고, safe fn은 외부 함수 하나의 전제 조건을 보증한다. 둘 다 grep으로 찾기 쉬워야 하고, Rust는 적어도 그 보증을 담은 블록에 unsafe라는 단어를 강제로 붙이니 한 번 검색하면 모든 주장이 나온다.

같은 질문의 다른 갈래

  1. age>=18=>allow와 Racket 리더

    규칙 작성자들이 age>=18=>allow처럼 공백도 괄호도 없이 쓰고 싶어 해요. 보기엔 작은 변화예요. 그런데 여기서 무엇이 토큰인지는 어느 단계에서 정하나요? Racket에서는 리더가 처음 구문 표현을 만들어요. 평범하…