【问题标题】:What is the rationale of Go not having the const qualifier?Go 没有 const 限定符的理由是什么?
【发布时间】:2015-11-17 03:41:47
【问题描述】:

我是一名 C++ 高级程序员。我目前正在做一些 Go 编程。我真正想念的唯一功能是 const 限定符。在 go 中,如果你想修改一个对象,你传递它的指针。如果您不想修改它,则按值传递它。但是如果结构很大,你应该通过指针传递它,这会覆盖不修改特性。更糟糕的是,您可以按值传递一个对象,但如果它包含一个指针,您实际上可以修改其内容,这会带来可怕的竞争条件危险。某些语言类型(例如地图和切片)具有此功能。这发生在一种应该为并发而构建的语言中。所以避免修改的问题在 Go 中真的不存在,你应该通过值传递 不包含指针 的小对象(你必须知道该对象不包含指针),如果他们不会被修改。

使用 const,您可以通过 const 指针传递对象,而不必担心修改。类型安全是关于拥有一个允许速度并防止与类型相关的错误的合同。另一个这样做的特性是 const 限定符。

【问题讨论】:

  • 请注意,您可以(而且恕我直言应该)通过接口解决此问题;您可以定义一个接口来公开数据结构的不可变视图。回复:“更糟糕的是,你可以通过值传递一个对象,但如果它包含一个指针,你实际上可以修改它的内容”:在 C++ 中也是如此;请注意,const被指针成员传递继承。
  • @ruakh 但是在 C++ 中,您可以防止对象被复制,或者在复制时应用所需的语义(引用计数、深度复制等)。接口技术是有效的。例如,您可以/应该传递 Reader 接口而不是 *File。谢谢。
  • @peterSO 埃里克老兄的回答很糟糕。这不是关于保证,而是关于合同。您可以在函数中对对象进行 const 转换并对其进行修改,就像在 Go 函数中放置 if random % 1000000 == 0 { panic() } 一样。使用只读接口技术,您可以转换为原始对象并对其进行修改。 你不应该做任何一个
  • 至少从one of the developers 开始,它会是“虽然以某种方式将对象标记为不可变可能有一些好处,但我们认为 const 类型限定符不是很重要。”跨度>

标签: go


【解决方案1】:

C/C++ 中的 const 类型限定符有多种含义。应用于变量时,意味着该变量是不可变的。这是一项有用的功能,也是 Go 中缺少的功能,但您似乎不是在谈论它。

您正在谈论 const 可以用作函数的部分强制合同的方式。函数可以为指针参数提供 const 限定符,以表示该函数不会使用该指针更改任何值。 (当然,除非函数使用强制转换(C++ 中的 const_cast)。或者,在 C++ 中,指针指向声明为 mutable 的字段。)

Go 有一个非常简单的类型系统。许多语言都有一个复杂的类型系统,您可以在其中通过编写类型来强制执行程序的正确性。在许多情况下,这意味着大量编程涉及编写类型声明。 Go 采用了不同的方法:您的大部分编程都涉及编写代码,而不是类型。您通过编写正确的代码来编写正确的代码,而不是通过编写捕获您编写不正确代码的情况的类型。如果您想捕获不正确的代码,您可以编写分析器,例如 go vet,用于查找代码中无效的情况。 Go 比 C/C++ 更容易编写这类分析器,因为语言更简单。

这种方法有优点也有缺点。 Go 在这里做出了明确的选择:编写代码,而不是类型。这不是每个人的正确选择。

【讨论】:

  • C++ 中的常量总是关于一个不可变值或指向一个不可变值的指针。我不认为 Go 是关于“通过编写正确的代码来编写正确的代码”,它主要是关于“除非非常必要,否则不要添加功能”。对于不可变的接口和包装器,它似乎不适用于 const。
【解决方案2】:

请将其视为扩展评论。我不是任何编程语言设计者,所以不能在这里深入细节,但作为 C++ 的长期开发者和 Go 的短期开发者,我将发表我的看法。

Const 对编译器来说是一项重要的功能,因此必须确保它是否为用户实现它提供了足够的优势,并且不会牺牲语法的简单性。您可能认为这只是我们所说的 const 限定符,但看看 C++ 本身,这并不容易——有很多警告。

  1. 你说const 是一个合同,你不应该在任何情况下修改它。您反对使用只读接口的一个论点是您可以将其转换为原始类型并做任何您想做的事情。你当然可以。与在 C++ 中使用 const_cast 对合约竖起中指的方式相同。出于某种原因,它被添加到语言中,我不确定我是否应该为它感到自豪,我用过一两次。

  2. C++ 中有另一个修饰符允许您放宽合同 - mutable。有人意识到const 结构实际上可能需要修改一些字段,通常是保护内部变量的互斥锁。我猜你在 Go 中需要类似的东西才能实现线程安全的结构。

  3. 只要简单const int x,人们就可以轻松关注。但是随后指针跳了进来,人们真的很困惑。 const int * xint * const xconst int * const x——这些都是x的有效声明,每个都有不同的合约。我知道选择正确的不是火箭科学,但是作为一名高级 C++ 程序员的经验是否告诉您人们广泛理解这些并且总是使用正确的?我什至没有提到像const int * const * * * const * const x 这样的东西。这让我大吃一惊。

在转到第 4 点之前,我想引用以下内容:

更糟糕的是,你可以通过值传递一个对象,但如果它包含一个指针, 你实际上可以修改它的内容

现在这是一个有趣的指控。 C++ 中也有同样的问题;更糟糕的是——即使您将 object 声明为 const,它仍然存在,这意味着您无法使用简单的 const 限定符来解决问题。看下一点:

  1. 根据 3 和指针,要表达正确的合同并不容易,有时事情会出乎意料。这段代码让一些人感到惊讶:

    struct S {
        int *x;
    };
    
    int main() {
        int n = 7;
        const S s = {&n}; // don't touch s, it's read only!
        *s.x = 666; // wait, what? s is const! is satan involved?
    }
    

    我相信上面的代码编译的原因对你来说很自然。这是您无法修改的指针值(它指向的地址),而不是它背后的值。你必须承认周围有人会扬起眉毛。

我不知道这是否有意义,但我一直在 C++ 中使用 const。非常精准。精神上去吧。不确定它是否曾经拯救过我,但在转向 Go 之后,我必须承认我从未错过它。考虑到所有这些边缘情况和例外情况,我真的相信像 Go 这样的极简语言的创造者会决定跳过这个。

类型安全是关于拥有允许速度并防止 类型相关的错误。

同意。例如,在 Go 中,我喜欢类型之间没有隐式转换。这确实让我避免了类型相关的错误

另一个这样做的特性是 const 限定符。

根据我的全部答案——我不同意。如果一个通用的 const 合约肯定会这样做,一个简单的 const 限定符是不够的。然后你需要一个 mutable 一个,也许是一种 const_cast 功能,但它仍然会给你留下误导性的保护信念,因为很难理解究竟什么是恒定的。

希望一些语言创造者会设计出一种完美的方式来在我们的代码中定义常量,然后我们会在 Go 中看到它。或者转移到新的语言。但就个人而言,我不认为 C++ 的方式特别好。

(替代方案是遵循函数式编程范式,它希望看到它们的所有“变量”都是不可变的。)

【讨论】:

  • 对,这就是我所说的扩展评论
  • 1.我不反对不可变接口。我反对 const 毫无价值的理由,因为您可以规避它。 2. 在可变性不会以有意义的方式(如互斥锁)影响 const 对象的状态的情况下,Mutable 为您提供了合同的例外。 3. 你不会在写得很好的程序中写出那样的代码。您可能会使用 typedefs 和类似的东西来澄清。 3.5.如果应该存在的话,您可以在构造函数中解决内部指针问题。 4.结构'S'需要一个构造函数。
  • C++ 的 const 是完美的,我从来没有遇到过任何问题。但是我意识到要使其有效,您可能需要其他东西,例如构造函数。目前,不可变接口是可行的方法。
猜你喜欢
  • 2011-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-13
  • 1970-01-01
  • 1970-01-01
  • 2017-05-20
相关资源
最近更新 更多