为简明起见,我们以一个简单的问题为例:1 米加 1 英尺等于多少米?,来看看 Go 语言如何处理这种计量单位不一致的问题。
这个问题的本质在于,一旦使用原始类型(如 float64
或 int
等)表示长度、时间、重量等带单位的数值,就极易导致单位混淆——不小心将“1 米”和“1 英尺”直接相加,算出 2 米,犯个低级错误。因此,关键在于如何巧妙地利用 Go 的类型系统,在编译阶段就杜绝这类失误。
一种方法是通过 类型别名(type
自定义类型) 来模拟计量单位。
// https://go.dev/play/p/Ui2UzlrZrA1
package main
import "fmt"
// 自定义类型:以 float64 为底层类型
type Meter float64
type Foot float64
// 定义转换常量
const footToMeter = 0.3048
// 为 Foot 添加 ToMeter 方法,Foot -> Meter 的转换方法
func (f Foot) ToMeter() Meter {
return Meter(float64(f) * footToMeter)
}
func main() {
var m Meter = 1.0
var f Foot = 1.0
// 即使底层都是 float64,也不能直接将 Meter 和 Foot 相加
// result := m + f
// 错误❌:invalid operation: m + f (mismatched types Meter and Foot)
// 正确✅:先将 Foot 转为 Meter
result := m + f.ToMeter()
fmt.Printf("1 米 + 1 英尺 = %.4f 米\n", result)
// 1 米 + 1 英尺 = 1.3048 米
}
但由于类型别名的底层都是相同的基础类型 float64
,因此通过类型转换,还是有机会计算出错误的结果:
errorResult := float64(m) + float64(f)
fmt.Printf("1 米 + 1 英尺 = %.4f 米\n", errorResult)
// 错误❌:1 米 + 1 英尺 = 2.0000 米
还有一种方法可用于处理计量单位不一致的问题,方法灵感来自 Go 标准库中的 time.Duration
类型。通过自定义类型 + 单位常量 + 乘法赋与单位的组合,即可实现可读性更强的数值操作方式:
// https://go.dev/play/p/UeOGbN2j9zK
package main
import (
"fmt"
)
// 定义一个新类型 Length,底层是 float64,用于表示带单位的长度
type Length float64
// 定义单位常量,米为基准长度单位
const (
Meter Length = 1.0 // 米,作为长度的基准单位
Foot Length = 0.3048 // 英尺,等于 0.3048 米
)
// 为 Length 类型定义一个方法,用于以米为单位输出数值
func (l Length) Meter() float64 {
return float64(l)
}
func main() {
m := 1 * Meter
f := 1 * Foot
// 将两个长度相加
result := m + f
fmt.Printf("1 米 + 1 英尺 = %.4f 米\n", result.Meter())
// 1 米 + 1 英尺 = 1.3048 米
}
但这种方法也有缺点,当长度存放在变量中,而不是通过字面量给出时,就不得不做类型转换,反倒降低了可读性:
func main() {
// 用户输入或从其他函数获取的原始值(没有明确给出单位,但已知单位是米)
m := 1.0
f := 1 * Foot
// 将两个长度相加
// result := m + f
// 错误❌:invalid operation: m + f (mismatched types float64 and Length)
// 正确✅:必须显式转换 float64 → Length
result := Length(m)*Meter + f
fmt.Printf("1 米 + 1 英尺 = %.4f 米\n", result.Meter())
}
这也是使用自定义类型 + 单位常量 + 乘法赋与单位这一“组合拳”的权衡点之一。
🔚
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。