【问题标题】:Why is rune in golang an alias for int32 and not uint32?为什么 golang 中的 rune 是 int32 而不是 uint32 的别名?
【发布时间】:2021-03-31 22:09:06
【问题描述】:

Go 中的 rune 类型是 defined

int32 的别名,在所有方面都等同于 int32。这是 按照惯例,用于区分字符值和整数 价值观。

如果打算用这种类型来表示字符值,为什么 Go 语言的作者不用uint32 而不是int32?当 rune 值为负数时,他们如何期望在程序中处理它?另一个类似的类型byteuint8 的别名(而不是int8),这似乎是合理的。

【问题讨论】:

  • 注意:byteuint8 的别名,而不是uint
  • 你之前选择了正确的答案,有什么变化?

标签: go


【解决方案1】:

我google了一下,发现this

这个问题已经被问过好几次了。 rune 占用 4 个字节,而不仅仅是一个,因为它应该存储 unicode 代码点,而不仅仅是 ASCII 字符。与数组索引一样,数据类型是有符号的,因此您可以在对这些类型进行算术运算时轻松检测溢出或其他错误。

【讨论】:

  • 该线程中的所有答案都认为有足够的空间以有符号的 32 位整数引用 Unicode 的所有代码点。因此,我确实了解符文如何大到足以解决 Unicode 范围。关于类型选择的问题仍然存在。为什么不使用 uint16(它具有可比较的正整数值范围)但只使用 int32 的一半空间?
  • @TapanKarecha:不过,uint16 并不适合所有的 Unicode。它适合其中很大一部分,但 Unicode 以 0x10fffd 结尾。
  • Christoph Hack:“这个问题已经被问过好几次了。rune 占用 4 个字节,而不仅仅是一个,因为它应该存储 unicode 代码点,而不仅仅是 ASCII 字符。像数组索引一样,数据类型是有符号的您可以在使用这些类型进行算术运算时轻松检测溢出或其他错误。”
  • @chendesheng,请将您的评论添加到您的答案中。在我看来,这是最重要的部分。
  • 是的:uinta=1b=2 (play) 时可能具有难以调试的行为,例如a-b > 1000。所以 Go 尽可能使用int
【解决方案2】:

它不会变成负数。目前 Unicode 中有 1,114,112 个代码点,与 2,147,483,647 (0x7fffffff) 相去甚远——即使考虑到所有保留块。

【讨论】:

  • 谢谢!尽管此时 rune 可以处理比 unicode 需要的范围大得多的范围,但问题是负值 可以 分配给 rune。如果它是无符号整数,则可以避免这种情况。但是对于符文仍然是有符号类型可能还有其他考虑因素,我想知道那些是什么。
  • @TapanKarecha:当然可以,但是您也可以在 Unicode 范围之外指定一个正值。两者都不是有效的 Unicode。 (负数可能更明显地作为错误条件检查,作为从 C 中获取的习惯?)
  • .@false:是的,类型范围的正端会有无效值,但是类型范围的两端都有无效值是我在处理概念时遇到的问题.正如你所说,如果类型是无符号的,我就不必担心检查负值,这在验证期间少了一次检查。
  • @TapanKarecha:不,我是说应该返回 Unicode 的东西的负返回值将是一个明显的错误(不是 Go 需要的东西,但你可能在其他语言中通常会这样做),但检查阳性一点也不方便。从Unicode’s stability policy 来看,这甚至是不可能的。
  • 我认为 chendesheng 的引用最能找到根本原因:Go 使用大量有符号值,不仅用于符文,还用于数组索引、Read/Write 字节数等。那是因为 @ 987654326@s,在任何语言中,都会表现得令人困惑,除非你保护每一个算术都不会溢出(例如 var a, b uint = 1, 2a-b > 0a-b > 1000000play.golang.org/p/lsdiZJiN7V)。 ints 在日常生活中表现得更像数字,这是使用它们的一个令人信服的理由,并且没有同样令人信服的理由不使用它们。
【解决方案3】:

Golang, Go : what is rune by the way?”提到:

在最近的 Unicode 6.3 中,定义了超过 110,000 个符号。这需要每个代码点至少有 21 位表示,所以 rune 就像 int32 并且有很多位。

但关于溢出或负值问题,请注意一些 unicode 函数如unicode.IsGraphic 的实现确实包括:

我们转换为uint32 以避免额外的阴性测试

代码:

const MaxLatin1 = '\u00FF' // maximum Latin-1 value.

// IsGraphic reports whether the rune is defined as a Graphic by Unicode.
// Such characters include letters, marks, numbers, punctuation, symbols, and
// spaces, from categories L, M, N, P, S, Zs.
func IsGraphic(r rune) bool {
    // We convert to uint32 to avoid the extra test for negative,
    // and in the index we convert to uint8 to avoid the range check.
    if uint32(r) <= MaxLatin1 {
        return properties[uint8(r)]&pg != 0
    }
    return In(r, GraphicRanges...)
}

这可能是因为符文应该是constant(如“Go rune type explanation”中所述,其中符文可能位于int32uint32 甚至float32 或...:它的 constant 值授权将其存储在任何numeric types)中。

【讨论】:

    【解决方案4】:

    除了上面给出的答案之外,这里是我为什么 Go 需要符文的两分钱。

    • GoLang 中的字符串是字节数组,每个字符都表示为一个字节。因此,与其他语言相比,GoLang 具有非常高的性能优势
    • 但是由于我们需要一种方法来表示无法用 8 位范围表示的 UTF-8 代码点,因此我们使用符文来表示它们。
    • 为什么是 int32 而不是 uint32?你可能会问。这样做是为了在对字符串进行操作时检测溢出。

    this 文章更详细地讨论了所有这些

    【讨论】:

    • “每个字符都表示为一个字节”:不,例如“ш”超过一个字节。
    【解决方案5】:

    允许负值这一事实让您可以定义自己的 rune sentinel values

    例如:

    const EOF rune = -1
    
    func (l *lexer) next() (r rune) {
        if l.pos >= len(l.input) {
            l.width = 0
            return EOF
        }
        r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
        l.pos += l.width
        return r
    }
    

    在 Rob Pike 的演讲中看到这里:Lexical Scanning in Go

    【讨论】:

      猜你喜欢
      • 2011-01-19
      • 1970-01-01
      • 2012-09-24
      • 2016-12-31
      • 2014-09-17
      • 1970-01-01
      • 2011-10-19
      • 1970-01-01
      相关资源
      最近更新 更多