I’m designing a library where I want to encourage users to declare a type that represents a file on disk and use it within the same lexical scope in which it’s declared. The reason for this is that my library provides functionality like move, which relocates the file. After such an operation, the original file reference would no longer accurately represent its location on disk.
If users pass a File instance by reference or copy it across different lexical scopes, there’s a risk that it might no longer correctly represent the file it was originally associated with.
Current Design
Here’s a simplified version of my protocol design:
public protocol Directory {
var location: URL { get }
}
public protocol File {
/// The name of the file.
var name: String { get }
/// The enclosing folder containing the file.
var enclosingFolder: any Directory { get }
}
public extension File {
/// The location of the file as a URL.
var location: URL {
enclosingFolder.location.appending(component: name, directoryHint: .notDirectory)
}
}
// etc
Issue: Preventing Copies of File
To address this, I wanted to suppress implicit copying by marking File as ~Copyable, allowing me to use consuming for methods that mutate the file. However, I encountered an issue:
Even though I suppress ~Copyable on File, concrete types conforming to File are not required by the compiler to also suppress ~Copyable. This means users could still implement File in a way that allows implicit copying, undermining the goal of ensuring that a file reference is only valid within a certain lexical scope.
Question
• Is this behaviour by design?
• If so, what would be the recommended approach to enforce ~Copyable at the protocol level and ensure that all conforming types follow suit?
• Alternatively, are there better ways to structure this API to prevent accidental misuse while keeping it ergonomic?
Any insights would be greatly appreciated!