1
1
# ` @noncopyable ` structs and enums
2
2
3
3
* Proposal: [ SE-NNNN] ( NNNN-noncopyable-structs-and-enums.md )
4
- * Authors: [ Joe Groff] ( https://github.com/jckarter ) , [ Michael Gottesman] ( https://github.com/gottesmm ) , [ Andrew Trick] ( https://github.com/atrick )
4
+ * Authors: [ Joe Groff] ( https://github.com/jckarter ) , [ Michael Gottesman] ( https://github.com/gottesmm ) , [ Andrew Trick] ( https://github.com/atrick ) , [ Kavon Farvardin ] ( https://github.com/kavon )
5
5
* Review Manager: TBD
6
6
* Status: ** Partially implemented** as ` @_moveOnly ` with ` -enable-experimental-move-only `
7
7
@@ -116,7 +116,7 @@ class SharedFile {
116
116
117
117
### Restrictions on use in generics
118
118
119
- Noncopyable types may have generic parameters:
119
+ Noncopyable types may have generic type parameters:
120
120
121
121
``` swift
122
122
// A type that reads from a file descriptor consisting of binary values of type T
@@ -127,20 +127,104 @@ struct TypedFile<T> {
127
127
128
128
func read () -> T { ... }
129
129
}
130
+
131
+ let byteFile: TypedFile<UInt8 > // OK
130
132
```
131
133
132
- However, at this time, noncopyable types are not allowed to conform to
133
- protocols, and they cannot be used as type arguments when instantiating
134
- generic types or calling generic functions. A value of noncopyable type also
135
- cannot be stored inside of an ` Any ` or other existential.
136
- (Lifting these limitations is discussed under Future Directions.)
134
+ However, at this time, noncopyable types themselves are not allowed to be used
135
+ as a generic type. This means a noncopyable type _ cannot_ :
136
+
137
+ - conform to protocols, like ` Error ` or ` Sendable ` .
138
+ - serve as a type witness for an ` associatedtype ` requirement.
139
+ - be used as a type argument when instantiating generic types or calling generic functions.
140
+ - be cast to ` Any ` or any other existential.
141
+ - be accessed through reflection.
142
+ - appear in a tuple.
143
+
144
+ The reasons for these restrictions and ways of lifting them are discussed under
145
+ Future Directions.
146
+
147
+ Since a good portion of Swift's standard library rely on generics, there are a
148
+ a number of common types and functions that will not work with today's
149
+ noncopyable types:
137
150
138
151
``` swift
139
152
// ERROR: Cannot use noncopyable type FileDescriptor in generic type Optional
140
153
let x = Optional (FileDescriptor (open (" /etc/passwd" , O_RDONLY)))
154
+
155
+ // ERROR: Cannot use noncopyable type FileDescriptor in generic type Array
156
+ let fds: [FileDescriptor] = []
157
+
158
+ // ERROR: Cannot use noncopyable type FileDescriptor in generic type Any
159
+ print (FileDescriptor (-1 ))
160
+
161
+ // ERROR: Noncopyable struct SocketEvent cannot conform to Error
162
+ @noncopyable enum SocketEvent : Error {
163
+ case requestedDisconnect (SocketPair)
164
+ }
165
+ ```
166
+
167
+ For example, the ` print ` function expects to be able to convert its argument to
168
+ ` Any ` , which is a copyable value. Internally, it also relies on either
169
+ reflection or conformance to ` CustomStringConvertible ` . Since a noncopyable type
170
+ can't do any of those, a suggested workaround is to explicitly define a
171
+ conversion to ` String ` :
172
+
173
+ ``` swift
174
+ extension FileDescriptor /*: CustomStringConvertible */ {
175
+ var description: String {
176
+ return " file descriptor #\( fd ) "
177
+ }
178
+ }
179
+
180
+ let fd = FileDescriptor (-1 )
181
+ print (fd.description )
182
+ ```
183
+
184
+ A more general kind of workaround to mix generics and noncopyable types
185
+ is to wrap the value in an ordinary class instance, which itself can participate
186
+ in generics. To transfer the noncopyable value in or out of the wrapper class
187
+ instance, using ` Optional<FileDescriptor> ` for the class's field would be
188
+ ideal. But until that is supported, a concrete noncopyable enum can represent
189
+ the case where the value of interest was taken out of the instance:
190
+
191
+ ``` swift
192
+ @noncopyable
193
+ enum MaybeFileDescriptor {
194
+ case some (FileDescriptor)
195
+ case none
196
+ }
197
+
198
+ class WrappedFile {
199
+ var file: MaybeFileDescriptor
200
+
201
+ enum Err : Error { case noFile }
202
+
203
+ init (_ fd : consuming FileDescriptor) {
204
+ file = .some (fd)
205
+ }
206
+
207
+ func consume () throws -> FileDescriptor {
208
+ if case let .some (fd) = file { // consume `self.file`
209
+ file = .none // must reinitialize `self.file` before returning
210
+ return fd
211
+ }
212
+ throw Err.noFile
213
+ }
214
+ }
215
+
216
+ func example (_ fd1 : consuming FileDescriptor,
217
+ _ fd2 : consuming FileDescriptor) -> [WrappedFile] {
218
+ // create an array of descriptors
219
+ return [WrappedFile (fd1), WrappedFile (fd2)]
220
+ }
141
221
```
142
222
143
- (more examples here)
223
+ All of this boilerplate melts away once noncopyable types support generics.
224
+ Even before then, one major improvement would be to eliminate the need to define
225
+ types like ` MaybeFileDescriptor ` through a noncopyable ` Optional `
226
+ (see Future Directions).
227
+
144
228
145
229
### Using values of ` @noncopyable ` type
146
230
0 commit comments