swift字面量

在写 Swift 的时候,遇到了一个看似平平无奇、但细想却有点“怪”的代码:

1
2
var char: Character = "A" 
var str: String = "A"

第一眼看上去没什么问题——一个是 Character,一个是 String,右边都是 “A”。

但问题来了:
👉 为什么同一个 “A”,既可以赋值给 Character,也可以赋值给 String

这背后其实牵扯到 Swift 一个非常核心但又经常被忽略的机制:字面量协议(Literal Protocols)。

一、直觉 vs 实际

如果用传统语言(比如 C / Java)的思维:

  • “A” 应该就是字符串
  • ‘A’ 才是字符

但 Swift 没有 ‘A’ 这种语法,只有 “A”。于是问题就变成:
👉 “A” 到底是 String,还是 Character?

答案是:

它一开始什么都不是。

二、字面量其实是“无类型”的

在 Swift 中:"A"并不是一个 String,而是一个字面量(literal)。
它的类型由上下文决定:

1
2
let a: String = "A"       // → String
let b: Character = "A" // → Character

也就是说:
👉 “A” 是一个“可被解释”的值,而不是固定类型的对象。

三、字面量协议

Swift 通过一整套协议,让不同类型可以“接收”字面量。
你可以把它理解成:
字面量 = 编译器帮你调用某个 init(...)

字符串

  • ExpressibleByStringLiteral
  • ExpressibleByExtendedGraphemeClusterLiteral
  • ExpressibleByUnicodeScalarLiteral
    这三个协议分别对应不同层次的字符串:
  • String 遵循 ExpressibleByStringLiteral
  • Character 遵循 ExpressibleByExtendedGraphemeClusterLiteral
  • UnicodeScalar 遵循 ExpressibleByUnicodeScalarLiteral

这三个的区别在于:

  • String 是一串字符的集合;
  • Character 是一个“扩展字形簇”(可能由多个 Unicode 标量组成),比如 emoji 表情和中文字符;
  • UnicodeScalar 是一个单一的 Unicode 标量。

示例:

1
2
3
let str: String = "A"          // 调用 String.init(stringLiteral:)
let char: Character = "A" // 调用 Character.init(extendedGraphemeClusterLiteral:)
let scalar: UnicodeScalar = "A" // 调用 UnicodeScalar.init(unicodeScalarLiteral:)

⚠️ 关键点
同一个字面量:"A",可以根据上下文走不同协议。

字符串插值

ExpressibleByStringInterpolation 协议让你可以在字符串字面量中插入变量和表达式:
支持这种语法:

1
2
let name = "Swift"
let s = "Hello \(name)"

你甚至可以自定义解析规则,实现 DSL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Log: ExpressibleByStringInterpolation {
init(stringLiteral value: String) {}

init(stringInterpolation: StringInterpolation) {}

struct StringInterpolation: StringInterpolationProtocol {
init(literalCapacity: Int, interpolationCount: Int) {}

mutating func appendLiteral(_ literal: String) {}

mutating func appendInterpolation(_ value: Int) {
print("int:", value)
}
}
}
let log: Log = "Value is \(42)" // 输出: int: 42

同样的,比如 SQL DSL 也可以通过字符串插值来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct SQL: ExpressibleByStringInterpolation {
init(stringLiteral value: String) {}

init(stringInterpolation: StringInterpolation) {}

struct StringInterpolation: StringInterpolationProtocol {
init(literalCapacity: Int, interpolationCount: Int) {}

mutating func appendLiteral(_ literal: String) {}

mutating func appendInterpolation(_ value: Int) {
print("value:", value)
}
}
}
let query: SQL = "SELECT * FROM users WHERE id = \(42)" // 输出: value: 42

整数、浮点数

同样的道理,整数和浮点数也有对应的协议:

  • ExpressibleByIntegerLiteral
  • ExpressibleByFloatLiteral

示例:

1
2
3
4
5
6
struct MyInt: ExpressibleByIntegerLiteral { 
init(integerLiteral value: Int) {
print("init with", value)
}
}
let x: MyInt = 42 // 输出: init with 42

Bool

ExpressibleByBooleanLiteral 协议,让你可以用 truefalse 来初始化自定义类型:

1
2
3
4
5
6
7
struct MyBool: ExpressibleByBooleanLiteral {
init(booleanLiteral value: Bool) {
print(value)
}
}

let b: MyBool = true // 输出: true

数组

ExpressibleByArrayLiteral 协议,让你可以用数组字面量来初始化自定义类型:

1
2
3
4
5
6
7
struct MyArray: ExpressibleByArrayLiteral {
init(arrayLiteral elements: Int...) {
print(elements)
}
}

let arr: MyArray = [1, 2, 3] // 输出: [1, 2, 3]

字典

ExpressibleByDictionaryLiteral 协议,让你可以用字典字面量来初始化自定义类型:

1
2
3
4
5
6
7
struct MyDict: ExpressibleByDictionaryLiteral {
init(dictionaryLiteral elements: (String, Int)...) {
print(elements)
}
}

let dict: MyDict = ["a": 1, "b": 2] // 输出: [("a", 1), ("b", 2)]

nil

ExpressibleByNilLiteral 协议,让你可以用 nil 来初始化自定义类型:

1
2
3
4
5
6
7
8
ExpressibleByNilLiteral
struct MyOptional: ExpressibleByNilLiteral {
init(nilLiteral: ()) {
print("nil")
}
}

let x: MyOptional = nil

总结

很多人会误以为这是“自动类型转换”:Character("A")
但其实:
❌ 不是运行时转换
✅ 是编译期选择构造方式

1
2
3
let a = 10
let b = "hello"
let c = [1, 2, 3]

👉 看起来是“语言内建”,实际上全部由协议驱动。
👉 Swift 的字面量,不是值,而是“构造语法”。


swift字面量
https://wttch96.github.io/post/swift/swift字面量.html
作者
Wttch
发布于
2026年4月17日
许可协议