为何用var声明的结构体传入函数后会变成let常量?
为什么用var声明的结构体实例传入函数后会被当作let常量?
这个问题刚好踩中了Swift里值类型和函数参数默认规则的核心,我来给你掰扯明白:
首先得明确两个关键知识点:
- 结构体是值类型:值类型在传递(比如传给函数参数)的时候,会创建一个完整的副本,函数里拿到的是和原实例完全独立的新对象,不是同一个东西。
- Swift函数参数默认是常量:不管你外部传入的是
var还是let修饰的实例,函数内部接收到的参数默认都是let常量,不允许修改——这是语言的默认设计,目的是避免你不小心修改副本,同时保证函数的行为更可预测。
结合你的代码来看:
- 外部的
var s = S()是可变的,因为你直接持有这个实例的所有权,所以可以自由修改它的属性。 - 但当你把
s传给changeS(s:)时,函数会创建一个s的副本,而且这个副本默认是let常量,所以你在函数里尝试s.i *= 2时,就会报错“赋值运算符左侧不可变:'s'是一个'let'常量”——本质是你不能修改一个常量结构体的属性。
如果想要在函数里修改这个实例(不管是副本还是原实例),有两种常见解决办法:
1. 把参数声明为可变(仅修改副本)
如果只是想在函数内部修改副本,不影响外部原实例,可以给参数加上var修饰符:
struct S { var i: Int = 1 } func changeS(var s: S) { s.i *= 2 // 现在合法了 print(s.i) // 输出2 } var s = S() changeS(s: s) print(s.i) // 输出1,原实例没被修改
2. 使用inout参数(修改原实例)
如果想要修改外部的原实例,需要用inout关键字标记参数,调用时还要在变量前加&符号:
struct S { var i: Int = 1 } func changeS(s: inout S) { s.i *= 2 // 合法 } var s = S() changeS(s: &s) print(s.i) // 输出2,原实例被修改
(注:inout对于值类型来说,本质是把实例拷贝进函数,修改后再拷贝回原变量,对外表现为直接修改原实例)
总结一下:你看到的现象,就是值类型的拷贝特性加上Swift函数参数默认是常量这两个规则共同作用的结果~
内容的提问来源于stack exchange,提问作者Raf
相关产品推荐
相关产品推荐

