Noncopyable types give compile-time guarantees for files, locks and tokens that must exist exactly once.
Normally every Swift type is implicitly Copyable. Since Swift 5.9 you can opt out with `~Copyable`: struct FileHandle: ~Copyable. Values of such a type have a single owner; assignment and passing move the value instead of copying it, and the compiler rejects any use of the old variable afterwards.
Noncopyable structs and enums can have a deinit, which runs exactly once when the owner's lifetime ends — ideal for closing a file descriptor or releasing a lock without a class or ARC.
Parameters of noncopyable type need an explicit ownership convention:
borrowing— the function gets read access temporarily; the caller keeps ownership.consuming— the function takes ownership; the caller can't use the value afterwards.inout— temporary exclusive mutable access.
The same words apply to methods: a consuming func close() ends the value's life, and inside it discard self skips the deinit when cleanup was already done manually.
The `consume` operator ends a variable's lifetime explicitly, even for copyable values: let b = consume a lets the optimizer avoid a copy and makes later use of a an error.
Generics can accept noncopyable types with <T: ~Copyable>, and since Swift 6 Optional and Result support them. Newer library types such as Mutex, Atomic, Span and InlineArray build on these features.
Example
struct TempFile: ~Copyable {
let fd: Int32
init(fd: Int32) { self.fd = fd; print("open", fd) }
borrowing func write(_ s: String) { print("write \(s) to", fd) }
consuming func persist() -> Int32 {
let kept = fd
discard self // skip deinit: file is kept
return kept
}
deinit { print("delete", fd) }
}
func log(_ f: borrowing TempFile) { f.write("log") }
do {
let a = TempFile(fd: 3) // open 3
log(a) // write log to 3
let b = a // move, not copy
// a.write("x") // error: 'a' used after consume
_ = b
} // delete 3
let kept = TempFile(fd: 4).persist()
print("kept", kept) // open 4, kept 4 (no delete)Remember
~Copyabletypes have one owner; assignment moves them- Parameters use
borrowing,consumingorinout - Noncopyable structs can have a
deinit;discard selfskips it
Watch out for
- Forgetting the ownership modifier on a noncopyable parameter, which the compiler requires.