Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why popFirst throws an error, but removeFirst works?

struct Queue<T>{

    private var elements : [T] = []

    public mutating func enqueue(_ element: T){
        elements.append(element)
    }
    public mutating func dequeue() -> T?{
        return elements.popFirst()  // ERROR!
    }
    public mutating func dequeue2() -> T?{
        return elements.removeFirst()
    }
}

The error I get for popFirst is:

cannot use mutating member on immutable value: 'self' is immutable

Both popFirst and removeFirst are marked as mutating and both return and T?. So why isn't it working?

EDIT: As others have commented, it seems to be some sort of bug. It's been discussed in the forums here.

EDIT: Still happens in Xcode 9.4.1 (Swift 4.1.2)

like image 686
mfaani Avatar asked Apr 08 '18 23:04

mfaani


1 Answers

The error is improved in Swift 4.2:

error: ios.playground:4:25: error: '[T]' requires the types '[T]' and 'ArraySlice<T>' be equivalent to use 'popFirst'
        return elements.popFirst()  // ERROR!
                        ^

You get the error because popFirst is not defined for all Collections. It's only defined if the Collection is its own SubSequence type. Here's the implementation:

extension Collection where SubSequence == Self {
  /// Removes and returns the first element of the collection.
  ///
  /// - Returns: The first element of the collection if the collection is
  ///   not empty; otherwise, `nil`.
  ///
  /// - Complexity: O(1)
  @inlinable
  public mutating func popFirst() -> Element? {
    // TODO: swift-3-indexing-model - review the following
    guard !isEmpty else { return nil }
    let element = first!
    self = self[index(after: startIndex)..<endIndex]
    return element
  }
}

The extension requires SubSequence == Self. Self (with a capital S) is the type of self (with a lower-case s). It is the type on which you're calling popFirst. In your code, Self is Array<T>, also spelled [T].

The constraint is necessary for this line of popFirst to compile:

self = self[index(after: startIndex)..<endIndex]

^__^   ^_______________________________________^
 |                         |
 |                        This is a SubSequence.
 |
This is a Self.

self[index(after: startIndex)..<endIndex] is a SubSequence.

Swift can only assign a SubSequence to Self if it knows that Self == SubSequence.

Array's SubSequence type is ArraySlice. Since ArraySlice is not the same type as Array, this extension doesn't apply to Array.

like image 197
rob mayoff Avatar answered Oct 12 '22 10:10

rob mayoff