[swift-evolution] [Oversight] Reference types allow mutating methods through generics

James Froggatt conductator at ntlworld.com
Tue May 3 15:09:20 CDT 2016


Thanks for the response, I agree this is currently the best solution. Unfortunately, it's not just as simple as just implementing each method, since without being able to call super, I have to fully reimplement the original behaviour, which at best seems like bad practice, and would break in future versions of Swift, and at worst could lead to hard-to-detect bugs right now.

To recap for anyone reading, protocol extensions currently apply mutating methods unmodified to reference types, as I found trying to make a reference-type collection. This results in the compiler disallowing ‘let’ when calling these functions, and allows methods to reassign the reference ‘self’ to a new object. The best solution is to manually implement each method, removing the mutating modifier, yet this workaround doesn't extend to generic code.

To fix this behaviour, we would need to distinguish between ‘true’ mutating functions, which reassign self, and ‘partially’ mutating functions, for use in generics and protocol extensions, which can reassign properties only.
Is there any support for making this change? Or are there any simpler solutions?

I did submit a bug report, but I'm pretty sure a decent fix is not possible without some evolution of the language regarding the mutating keyword, so I'm trying to bring this up here in hope of us getting an actual solution. I've changed the title to what I hope is something that better reflects the problem; this thread was originally titled ‘[swift-evolution] [Bug?] Reference types and mutating methods’.


PS: I have noticed another side-effect of calling mutating functions on my reference-type collection: it seems to trigger didChange on properties, even when, upon comparing the new and old objects, the reference isn't being changed. I haven't done much experimentation with this behaviour; this may be an unexpected side-effect of an extension method assigning to self, but it feels like it could be undefined behaviour.

From James F

> On 30 Apr 2016, at 16:38, T.J. Usiyan <griotspeak at gmail.com> wrote:
> 
> The problem here seems to be with using the default implementation provided. If you override `append` in ObservedArray, the compiler allows it. That seems 'safe' but odd at first. I wouldn't *want* to implement every mutating method, but that is the current solution. I haven't puzzled out the reasoning behind this myself.
> 
> 
> ``` swift
> class ObservedArray<T> : ArrayLiteralConvertible {
>     var value: [T]
>     init(value: [T]) {
>         self.value = value
>     }
>     required init() {
>         self.value = []
>     }
> 
>     required convenience init(arrayLiteral elements: T...) {
>         self.init(elements)
>     }
> 
> }
> 
> extension ObservedArray {
>     typealias Index = Int
> 
>     var startIndex: Index {
>         return value.startIndex
>     }
> 
>     var endIndex: Index {
>         return value.endIndex
>     }
> 
>     subscript(position: Index) -> T {
>         return value[position]
>     }
> 
> }
> 
> extension ObservedArray : RangeReplaceableCollectionType {
>     typealias Generator = IndexingGenerator<[T]>
> 
>     func generate() -> Generator {
>         return value.generate()
>     }
> }
> 
> extension ObservedArray {
>     func replaceRange<C : CollectionType where C.Generator.Element == Generator.Element>(subRange: Range<Index>, with newElements: C) {
>         value.replaceRange(subRange, with: newElements)
>     }
> 
>     func append(newElement: T) { // <- adding this makes it work
>         value.append(newElement)
>     }
> }
> 
> let array: ObservedArray<String> = []
> array.append("1")
> 
> 
> ```
> 
> 
> 
> 
>> On Sat, Apr 30, 2016 at 7:52 AM, James Froggatt via swift-evolution <swift-evolution at swift.org> wrote:
>> I don't believe this has been addressed, please correct me if I'm wrong.
>> 
>> --My Situation--
>> I've recently been working on an observable collection type. Because each stores ‘subscriptions’ to changes that occur, it made sense to me that this should be a reference type, so subscriptions can't be copied with the values themselves.
>> 
>> I have made this class conform to RangeReplaceableCollectionType, providing it with all the standard collection functions. I do the following:
>> 
>> let array: ObservedArray<String> = []
>> array.append("1") //Error: Cannot use mutating member on immutable value: ‘array’ is a ‘let’ constant
>> 
>> I have to make the reference immutable just to use my new collection type? This is a bit of a deal-breaker.
>> 
>> --The Problem--
>> Mutating methods allow ‘self’ to be reassigned, which is just another way to mutate a value type. However, reassigning ‘self’ has a special meaning to reference types, which is presumably the reason they are disallowed in classes.
>> 
>> However, classes can conform to protocols with mutating methods, leading to the compiler disallowing calls to mutating methods for ‘let’ values of type ‘protocol<MutatingProtocol, AnyObject>’, which can be an annoyance in generic code. In addition, classes can inherit mutating methods from protocol extensions, leading to the behaviour I describe above.
>> 
>> Is this intentional behaviour? Am I going about this in the wrong way? Or is this really an omission in the language?
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160503/ee0c5a96/attachment.html>


More information about the swift-evolution mailing list