All the usual ways of unwrapping an Optional value that conforms to Copyable end up causing a copy:
struct MyStruct {
var value: Int
var existential: Any
}
// all variants cause a copy of MyStruct
func getValue1(from optionalStruct: MyStruct?) -> Int? {
switch optionalStruct {
case .some(let myStruct):
myStruct.value
case .none: nil
}
}
func getValue2(from optionalStruct: MyStruct?) -> Int? {
optionalStruct.map { $0.value }
}
func getValue3(from optionalStruct: MyStruct?) -> Int? {
if optionalStruct == nil {
return nil
} else {
return optionalStruct.unsafelyUnwrapped.value
}
}
func getValue4(from optionalStruct: MyStruct?) -> Int? {
if optionalStruct == nil {
return nil
} else {
return optionalStruct!.value
}
}
func getValue5(from optionalStruct: MyStruct?) -> Int? {
optionalStruct?.value
}
For example, getValue1 produces assembly like this (notice the copy):
output.getValue1(from: output.MyStruct?) -> Swift.Int?: # @"output.getValue1(from: output.MyStruct?) -> Swift.Int?"
push rbx
sub rsp, 80
lea rsi, [rsp + 40]
call outlined init with copy of output.MyStruct?
cmp qword ptr [rsp + 72], 0
je .LBB13_1
movups xmm0, xmmword ptr [rsp + 40]
movups xmm1, xmmword ptr [rsp + 56]
movaps xmmword ptr [rsp], xmm0
mov rax, qword ptr [rsp + 72]
mov qword ptr [rsp + 32], rax
movaps xmmword ptr [rsp + 16], xmm1
mov rbx, qword ptr [rsp]
mov rdi, rsp
call outlined destroy of output.MyStruct
xor edx, edx
jmp .LBB13_3
.LBB13_1:
mov dl, 1
xor ebx, ebx
.LBB13_3:
mov rax, rbx
add rsp, 80
pop rbx
ret
In fact, all of the versions above generate a call to something like outlined init with copy of output.MyStruct?.
The only way I've found to avoid the copy is to use the underscore-prefixed method Optional._borrowingMap:
func getValue(from optionalStruct: MyStruct?, key: Int) -> Int? {
optionalStruct._borrowingMap { $0.value }
}
Which produces much cleaner assembly without a copy:
output.getValue(from: output.MyStruct?, key: Swift.Int) -> Swift.Int?:
cmp qword ptr [rdi + 32], 0
je .LBB13_1
mov rax, qword ptr [rdi]
xor edx, edx
ret
This appears to leverage the relatively new borrowing switch feature (SE-0432), but that capability is currently only available either for ~Copyable types or in a generic context where the type is not required to be Copyable.