Mutating func trong Struct và enum – Swift

by DaoNM2
291 views

Như mọi người đã biết, struct và enum trong Swift là value types(kiểu giá trị), mặc định thì các thuộc tính của kiểu giá trị thì không thể được sửa đổi ở bên trong các phương thức thể hiện của nó(instance methods).

Tuy nhiên nếu chúng ta cần phải chỉnh sửa các thuộc tính của struct hoặc enum trong một phương thức cụ thể, thì chúng ta sẽ đặt mutating trước các func, nó sẽ giúp các func của bạn có thể thay đổi được các thuộc tính bên trong func và khi kết thúc func giá trị sẽ được nghi lại vào các thuộc tính của struct ban đầu. Phương thức này cũng có thể gán lại một instance mới cho thuộc tính self của nó và nó sẽ được thay thế khi phương thức kết thúc.

Mutating trong Struct

Trong ví dụ này mình sẽ tạo một Struct có tên là Counter và tạo ra một func lấy giá trị của thuộc tính count trong Counter như sau:

struct Counter {
    private var count: Int = 0
    
    func getCount() -> Int {
        count
    }
}

Đây là một ví dụ bình thường về struct, hàm getCount() ở đây không thực hiện thay đổi giá trị của struct Counter mà nó chỉ lấy giá trị của thuộc tính count theo cách thông thường.

Vậy khi chúng ta muốn viết một hàm increase() để tăng giá trị count thông thường chúng ta sẽ viết như sau:

Nếu là class thì sẽ không vấn đề gì vì class là reference type. Ở trường họp này do chúng ta đang viết một func chỉnh sửa thuộc tính count của struct Counter nên xCode sẽ báo lỗi rằng self ở đây là immutable(không thể thay đổi), như đã giải thích ở trên thì struct là value type nên mặc định sẽ không thể thay đổi được thuộc tính của nó trong các func của struct đó.

func getCount() không bị báo lỗi vì func này không làm thay đổi thuộc tính trong struct.

Để func increase() không bị báo lỗi chúng ta cần thêm mutating đằng trước func để xCode biết là func này có thể thay đổi được thuộc tính của struct:

struct Counter {
    private var count: Int = 0
    
    func getCount() -> Int {
        count
    }
    
    mutating func increase() {
        count += 1
    }
}

var counter = Counter() // count = 0
counter.increase() // count = 1

Để có thể thay đổi được thuộc tính của instance counter thì chúng ta cần phải khai báo nó là var, vì func increase() sẽ thay đổi giá trị của counter vì vậy cần khai báo là var để mutating func có thể gán lại giá trị mới cho instance counter.

Nếu chúng ta để là let Xcode sẽ thông báo lỗi không thể sử dụng mutating func trên giá trị không thể thay đổi, counter đang là một “let” constant. Do struct là value type nên nó là immutable có nghĩa là không thay đổi được, nếu chúng ta cố tình khai báo let count Xcode sẽ thông báo lỗi.

Lỗi khi khai báo let counter để call mutating func

NOTE: Để hiểu rõ hơn các bạn có thể xem thêm thông tin ở đây: Stored Properties of Constant Structure Instances

Mutating func không chỉ thay đổi được thuộc tính của struct mà nó còn có thể thay đổi cả giá trị của chính instance (self)

struct Counter {
    private var count: Int = 0
    
    func getCount() -> Int {
        count
    }
    
    mutating func increase() {
        count += 1
    }
    
    mutating func resetCounter() {
        self = Counter(count: 0)
    }
}

var counter = Counter() // count = 0
counter.increase() // count = 1
counter.resetCounter() // count = 0

mutating func resetCounter() là một ví dụ, ở trong func này chúng ta thực hiện tạo ra một instance Counter mới với giá trị khởi tạo là 0 và gán lại cho chính instance gọi func này.

Mutating func trong enum

Tương tự như struct, enum cũng là value type và để thay đổi giá trị trong func chúng cũng cần phải sửa dụng mutating cho func đó.

Để hiểu rõ hơn ta đi vào ví dụ sau:

Chúng ta cần tạo ra một công tắc quạt với một tính năng là mỗi khi bấm nút thì sẽ làm thay đổi tốc độ quay của quạt một cách tuần tự và lặp đi lặp lại. Để làm theo yêu cầu chúng ta sẽ tạo enum như sau:

enum FanStateSwitch {
    case off, low, high
    mutating func next() {
        switch self {
        case .off:
            self = .low
        case .low:
            self = .high
        case .high:
            self = .off
        }
    }
}

var fanSwitch = FanStateSwitch.off
fanSwitch.next() // fanSwitch is low
fanSwitch.next() // fanSwitch is high
fanSwitch.next() // fanSwitch is off

Tương tự như struct khi khởi tạo enum hãy nhớ khởi tạo nó với var thay vì let.

Hi vọng bài viết sẽ giúp các bạn hiểu rõ hơn về mutating func và cách sử dụng, ứng dụng nó vào trong dự án.

Chúc các bạn thành công!

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.

You may also like