Demystifying NonCopyable Type

Reshma Unnikrishnan
4 min readSep 16, 2023

--

The latest version of Swift, version 5.9, provides impressive updates such as NonCopyable, Macros, if and switch expressions, and more. To access these features, it is necessary to install the Swift 5.9 Snapshots on Xcode 14 or the Xcode 15 Beta.

SE-0390 introduces the concept of non-copyable structs and enums. An instance of noncopyable type always has unique ownership, unlike normal Swift types which can be freely copied.

This ensures that a value of a noncopyable type always has unique ownership, and the value cannot be copied.

All currently existing types in Swift are copyable , meaning it is possible to create multiple identical, interchangeable representations of any value of the type.

Why we have noncopyable type when there is class type ?

Classes can represent a unique resource, since an object has a unique identity once initialized, and only references to that unique object get copied. However, because the references to the object are still copyable, classes always demand shared ownership of the resource. This leads to own set of issues such as heap allocation overhead (since the overall lifetime of the object is indefinite), reference counting complexities, and shared ownership complications.

Here come the noncopyablein scene. Noncopyable type always have unique ownership, and can never be copied. This will fix the constraints such as suppressing implied generic constraints.

Lets start playing with Copyable

1. Declare noncopyable types

Noncopyable change introduces new syntax to suppress the Copyable requirement. ~Copyable .

  • ~means negation. The syntax means that “this type cannot be copied”. This type of Generic Constraints are not available elsewhere yet.

Sample of how a noncopyable struct looks like:

struct Vehicle: ~Copyable {
var name: String
var type: String
}

If you have a ~Copyable struct as property, the main Object must be also declared as ~Copyable .

~Copyable datatype as property

2. Restrictions for nonCopyable types

The noncopyable types cannot conform to the following areas as of now:

  1. protocols
  2. generic parameters
  3. associated type requirements in protocols
  4. the Self type in a protocol declaration, or in extensions

Lets try with a small example :

As you can see the error, of not finding Copyable on scope.

3. Ownership transfer for nonCopyable types

Currently We are checking on how the struct Vehicle that is a noncopyable type as seen below. This piece of code is very normal practice in Copyable datatype.

 
func createVehicle() {
let newVehicle = Vehicle()
let anotherVehicle = newVehicle
print(anotherVehicle.value)
}

What will be the status of newVehicle here?

As we are assigning newVehicle to anotherVehicle, the ownership is passed to anotherVehicle and If you try to print the newVehicle value print(newVehicle.value) then you will see this error : ‘newVehicle’ used after consume .Means newVehicle value is consumed, and is no longer be used as the ownership moved to anotherVehicle.

Used after consume error

What would be the status if pass the noncopyableas a function parameter?

    
func printVehicle(vehicle: Vehicle) {
var porsche = Vehicle()
}

It shows with an error message from Xcode. ie Noncopyable parameter must specify its ownership. There are three possibilties:

  1. Add ‘borrowing’ for an immutable reference
  2. Add ‘inout’ for a mutable reference
  3. Add ‘consuming’ to take the value from the caller

When specifying borrowing?

If you specify borrowing , the argument newVehicle is immutableTherefore, newVehicle cannot change the value in the method.Also when you try to change the value inside the function, there will be an error popping ie, `Cannot assign to property: ‘vehicle’ is a ‘let’ constant`

Ownership itself is held by the caller of the method. The callee (Callee) only temporarily borrows ownership.

When specifying consuming?

When this method printVehicleis called, the ownership that was in the instance newVehicleis lost and its lifeCycle ended here and ownership is transferred to the vehicle (the function property) instead.

If we tried to print the newVehicle value after calling print(vehicle: newVehicle) , you will get a compilation error. (as lifeCycle is ended for newVehicle)

Other notable feature of this proposal is the addition of deinit declarations for noncopyable structs and enums. Previously, this capability was only available for classes. With this addition, it would be possible for individual instances to perform specific actions at the end of lifeCycle, allowing for greater control and efficiency.

deinit function

Happy Coding!

--

--