본문 바로가기
Swfit

Structure의 immutability

by GGShin 2024. 4. 21.

value 타입인 struct의 인스턴스는 let로 선언되는 경우, 내부의 변수가 var로 선언되어 있다 할지라도 해당 변수의 값을 변경할 수 없습니다. 

struct MyStruct {
    var id: Int
}

let structA = MyStruct(id: 0)
structA.id = 1 // Error: Cannot assign to property: 'structA' is a 'let' constant

 

struct 인스턴스 자체를 var로 선언해야만 내부 값을 변경할 수 있습니다.

 

왜 그럴까?

이는 struct가 value 타입인 것과 연관이 있습니다. value 타입인 struct는 내부의 변수 값이 변경될 때, 기존의 인스턴스의 값이 변하는 것이 아니라 마치 새로운 인스턴스가 생성되는 것 처럼 동작합니다. 매번 값이 변경될 때마다 기존 인스턴스를 없애고 다시 만드는 것은 비용이 비교적 많이 들기 때문에 실제로는 변경된 값만 바뀌게 되지만, '의미론적'으로는 struct 자체가 새로운 값이 되는 것처럼 작동하게 됩니다.

 

computed property를 보면 쉽게 확인할 수 있습니다.

struct MyStruct {
    var id: Int
}

class MyClass {
    var foo: MyStruct {
        didSet {
            print("Foo has been changed.")
        }
    }
    
    init(foo: MyStruct) {
        self.foo = foo
    }
}

let bar = MyClass(foo: MyStruct(id: 0))
bar.foo.id = 1
//"Foo has been changed."가 출력됩니다.

 

위의 예시에서 bar.foo 값이 아닌 bar.foo.id 값을 바꾸어 주었을 뿐인데도 didSet이 동작한 것을 볼 수 있습니다. 이를 통해서 struct의 내부 변수의 값을 업데이트 하는 것이 마치 struct 자체가 새로이 생성된 것과 같다는 사실을 확인할 수 있습니다. 

 

다시 기존의 이야기로 돌아가면, 이러한 특성 때문에 let으로 선언된 struct는 내부 변수의 값을 변경할 수 없는 것입니다. 내부 변수의 값이 변경되면 새로운 struct 인스턴스가 생성되는 것처럼 작동하는데, let이면 기본적으로 변수에 새로운 값이 할당될 수 없기 때문입니다. 따라서 struct는 let으로 선언될 때, 내부 변수가 var으로 선언되어 있을 지라도 변할 수 없도록 설정되는 것입니다.

 

struct 내부 method에 붙이는 mutating는 왜 필요할까?

앞선 내용에서 struct 인스턴스가 let으로 선언되어 있으면 내부 변수 값을 변경할 수 없다는 것을 알게 되었습니다. 하지만 개발자가 struct를 정의하는 시점에 compiler는 해당 struct 객체가 var로 선언될 지, let으로 선언될 지 알 수가 없습니다.

 

그렇기 때문에 기본적으로는 수정이 일어나지 못하도록 내부 method를 제한하게 됩니다. 만일 struct 내부에 선언된 function에 내부 변수 값을 업데이트하는 로직이 들어가 있다면, compiler가 에러를 띄우게 됩니다. 혹시라도 객체가 let으로 선언 되었을 경우, 해당 method가 호출되면 let으로 선언된 것이 의미가 없어지게 되기 때문입니다.

 

따라서 struct 내부에 변수 값을 업데이트하는 로직이 담긴 method를 정의하고자 한다면 "mutating"이라는 키워드를 앞에 붙여서 compiler에게 알려주는 것입니다. 만일 let으로 선언된 struct 객체에 mutating이 붙은 method를 호출하려고 할 때, compiler가 확인하고 호출을 제한할 수 있게 됩니다. 

 

struct MyStruct {
    var id: Int
    
    mutating func changeId(_ newId: Int) {
        self.id = newId
    }
}
let bar = MyStruct(id: 0)
bar.changeId(1) //Error: Cannot use mutating member on immutable value: 'bar' is a 'let' constant

 

 

참고자료

https://forums.swift.org/t/why-are-structs-in-swift-said-to-be-immutable/55319/15

 

Why are structs in Swift said to be immutable?

If you call that function on global itself, you will have overlapping write-read accesses, since: So self is considered as being modified for the entire body of the function. Within the function, you can read/write any instance properties on self; they are

forums.swift.org

 

https://www.hackingwithswift.com/sixty/7/5/mutating-methods

 

반응형