-
[SE-0518] tide-SendableSwift 2026. 4. 5. 08:26
안녕하세요. 그린입니다 🍏
이번 포스팅에서는 SE-0518 — ~Sendable로 non-Sendable 타입을 명시적으로 표현하기에 대해 정리해보겠습니다 🙋🏻

Intro
- Proposal: SE-0518
- Status: Implemented (Swift 6.4)
- Experimental Feature Flag: TildeSendable
Motivation
public 타입이 Sendable을 명시적으로 conform하지 않을 때, 그 의도를 파악하기가 쉽지 않습니다.
아직 Sendable conformance를 추가하지 않은 건지, 아니면 의도적으로 non-Sendable로 설계한 건지가 불분명하거든요.
이를 판단하려면 타입의 스토리지 구조와 동기화 메커니즘 같은 구현 세부사항을 알아야 하는데, 라이브러리 외부에서는 접근하기 어렵습니다.또한 클래스 자체는 non-Sendable이지만, 특정 서브클래스는 Sendable이어야 하는 상황도 있습니다.
기존에는 이를 아래처럼 unavailable extension으로 표현했는데요.class Base { // ... } @available(*, unavailable) extension Base: Sendable { }문제는 unavailable conformance는 서브클래스에 그대로 상속된다는 점입니다.
그래서 thread-safe 서브클래스를 만들려고 해도,final class ThreadSafe: Base, @unchecked Sendable { // ... }다음과 같은 경고가 발생합니다.
warning: conformance of 'ThreadSafe' to protocol 'Sendable' is already unavailable서브클래스가 Sendable이 될 수도 있고 아닐 수도 있는, 이 세 번째 상태를 언어 차원에서 명확하게 표현할 방법이 없었던 거죠.
라이브러리 작성자 입장에서는 API의 Sendable 판단을 체계적으로 하기도 어렵고, 클라이언트에게 의도를 전달하기도 힘든 상황이었습니다.
Solution
SE-0518은 ~Sendable conformance 문법을 도입해 타입이 명시적으로 non-Sendable임을 나타낼 수 있게 합니다.
// `ExecutionResult`는 감사 결과 non-`Sendable`로 명시. // non-Sendable associated value를 가진 케이스가 있기 때문입니다. public enum ExecutionResult: ~Sendable { case success // ... // ... case failure(NonSendable) // ... } // `Base`는 감사 결과 non-`Sendable`로 명시. // 서브클래스는 non-`Sendable` mutable state를 추가하거나, // 상태를 보호하거나 상수로 만들어 `Sendable`로 표시될 수 있습니다. public class Base: ~Sendable { // ... } // 이 서브클래스는 감사 결과 `Sendable`로 판단됩니다. public class ThreadSafeImpl: Base, @unchecked Sendable { // `Base`의 `value`를 lock 등을 통해 안전하게 접근합니다. } // 이 서브클래스는 mutable non-`Sendable` state를 가지므로 non-`Sendable`입니다. public class UnsafeImpl: Base { var x: NonSendable }~Sendable 문법은 타입에만 적용 가능합니다. 제네릭 파라미터 같은 다른 선언은 기본적으로 이미 ~Sendable이기 때문입니다.
Detailed Design
~Sendable은 ~Copyable, ~Escapable, ~BitwiseCopyable과 같이 tilde(~) 접두사를 사용해 conformance 억제를 표현합니다.
기본 사용
// Sendable inference 억제 struct NotSendableType: ~Sendable { let data: String } // 다른 conformance와 함께 사용 가능 struct MyType: Equatable, ~Sendable { let id: UUID } // 클래스에도 적용 가능 class MyClass: ~Sendable { private let data = 0 }
Extension에서는 사용 불가
억제 선언은 반드시 타입 선언부에서 해야 합니다.
Extension에서 사용하면 기존 코드의 의미가 바뀔 위험이 있거든요.
extension Test: ~Sendable {} // Error!
제네릭 파라미터, 프로토콜에는 사용 불가
억제할 implied Sendable 요구사항이 없기 때문에 거부됩니다.
protocol P: ~Sendable {} // Error! struct Test<T: ~Sendable> {} // Error! func test<T: ~Sendable>(_: T) {} // Error!
Sendable 요구사항을 만족시킬 수 없음
unavailable extension과 마찬가지로, ~Sendable 타입은 Sendable 요구사항을 만족시킬 수 없습니다.
func processData<T: Sendable>(_ data: T) { } struct NotSendable: ~Sendable { let value: Int } processData(NotSendable(value: 42)) // error: type 'NotSendable' does not conform to the 'Sendable' protocol
서브클래스에는 영향 없음
unavailable extension과의 가장 큰 차이점입니다.
~Sendable은 서브클래스에 상속되지 않습니다.
class A: ~Sendable { } final class B: A, @unchecked Sendable { } func takesSendable<T: Sendable>(_: T) { } takesSendable(B()) // Ok!
Sendable과 ~Sendable 동시 사용 불가
무조건적으로 둘 다 conform하려 하면 컴파일 오류가 발생합니다.
// actor는 항상 `Sendable`입니다. actor A: ~Sendable { // error: cannot both conform to and suppress conformance to 'Sendable' } struct Container<T>: ~Sendable { let value: T } extension Container: Sendable {} // error: cannot both conform to and suppress conformance to 'Sendable'상위 클래스나 프로토콜에서 상속된 명시적/파생 Sendable conformance에도 동일하게 적용됩니다.
protocol IsolatedProtocol: Sendable { } struct Test: IsolatedProtocol, ~Sendable { // error: cannot both conform to and suppress conformance to 'Sendable' } @MainActor class IsolatedBase { // global actor isolated 타입은 `Sendable`입니다. } class Refined: IsolatedBase, ~Sendable { // error: cannot both conform to and suppress conformance to 'Sendable' }
조건부 conformance는 허용
extension Container: Sendable where T: Sendable {} // Ok!조건부 Sendable conformance는 여전히 유용하거든요.
API 작성자가 타입이 특정 조건에서만 Sendable임을 표현하고 싶을 때 활용할 수 있습니다.
ExplicitSendable 추적 플래그
Swift 컴파일러는 public 타입의 sendability를 감사할 수 있는 방법을 제공합니다.
기존에는 -require-explicit-sendable 플래그를 사용했는데, 이번 제안에서 ~Sendable을 지원하도록 업데이트되고 ExplicitSendable 진단 그룹으로 전환되었습니다.
기본적으로 비활성화되어 있으며, -Wwarning ExplicitSendable로 활성화할 수 있습니다.
Alternatives Considered
@nonSendable 속성
@nonSendable struct MyType { let value: Int }이 방법도 고려됐지만, 프로토콜 conformance 방식이 더 ergonomic하고,
~Copyable, ~Escapable 같은 기존 conformance 억제 컨벤션과도 일관성이 있기 때문에 채택되지 않았습니다.
Future Directions
이번 제안은 Sendable에만 집중했지만, Equatable, Hashable, RawRepresentable 같이 암묵적으로 추론되는 다른 프로토콜 conformance에도 ~ 문법이 적용될 수 있습니다.
각각 고유한 특이점이 있어 별도 제안이 필요할 것으로 보입니다.
Conclusion
~Sendable은 작지만 굉장히 실용적인 추가입니다.
기존에는 non-Sendable 의도를 표현하려면 unavailable extension이라는 다소 우회적인 방법을 써야 했고,
서브클래스에 상속된다는 부작용까지 감수해야 했거든요.이제 ~Sendable로 그 의도를 명확하게 선언하면서, 서브클래스의 Sendable 가능성도 열어둘 수 있게 되었습니다.
라이브러리 작성자 입장에서는 API의 Sendability 추적 및 판단을 훨씬 체계적으로 할 수 있을 것 같습니다 🙌
References
swift-evolution/proposals/0518-tilde-sendable.md at main · swiftlang/swift-evolution
This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - swiftlang/swift-evolution
github.com
'Swift' 카테고리의 다른 글
[SE-0519] Borrow and Inout types for safe, first-class references (2) 2026.04.11 Swift 6.3 (0) 2026.03.29 Swift 6.2.4 (0) 2026.03.22 [SE-0515] Allow reduce to produce noncopyable results (1) 2026.03.14 [SE-0514] Hashable Conformance for Dictionary.Keys, CollectionOfOne and EmptyCollection (0) 2026.03.02