【问题标题】:Error: undefined method 'x' for Nil (compile-time type is (Point | Nil))错误:Nil 的未定义方法“x”(编译时类型为 (Point | Nil))
【发布时间】:2020-04-14 15:08:16
【问题描述】:

我正在编写一个测试,检查一个点的坐标是否具有某个值,例如:

it "should work" do
   p = do_something   # returns a Point(x, y)
   p.x.should eq 0    # errors (see below)
end

但是编译失败,报如下错误:

Error: undefined method 'x' for Nil (compile-time type is (Point | Nil))

我能够将问题简化为以下无法编译的最小示例:

struct Point
    property x : Int32
    property y : Int32
    def initialize(@x, @y)
    end
end

begin
    p = Point.new 0, 0
ensure
    p.x == 0
end

抛出同样的错误:

❯ crystal src/debug.cr
Showing last frame. Use --error-trace for full trace.

In src/debug.cr:11:7

 11 | p.x == 0
        ^
Error: undefined method 'x' for Nil (compile-time type is (Point | Nil))

现在,我在编程语言 Github 跟踪器上遇到了类似的错误报告:Nil type check fails when using ensure,显然这是一个必须通过 Crystal 语言解决的问题。

我的问题是,如何检查p.x 的值而不在ensure 块中触发此错误?我有点不知道如何访问它。

对于上下文,我正在编写一个加密库,它对椭圆曲线上的点进行操作,所以这里就是检查坐标的一切。

【问题讨论】:

    标签: unit-testing compiler-errors crystal-lang


    【解决方案1】:

    问题是编译器无法知道变量是否实际定义在 ensure 块中,如果你的 Point 构造函数中的某些东西抛出异常怎么办?

    考虑一下:

    def m
      raise "foo"
      bar = 0
    ensure
      bar += 1
    end
    

    在 Crystal 中这是一个编译时错误,在 Ruby(和类似语言)中它会成为一个运行时错误。

    【讨论】:

      【解决方案2】:

      这里有几种可能性,例如您可以将p.x == 0 行替换为:

      1. p.try &.x == 0 - p 将被检查为 Nil,只有当它不是 Nil 时,才会运行比较。
      2. p.not_nil!.x == 0 - 你命令编译器 p 永远不能是 Nil,但如果它实际上恰好是 nil,该行将在运行时引发。

      【讨论】:

      • 成功了。我认为p.x == 0 if !p.nil? 也成功了。
      • @Afr p.x == 0 if p 在这里也是等价的,因为 nil 是假的。这里的编译器错误是正确的,Point.new 可能会引发异常,在这种情况下,p 永远不会被设置。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-07
      • 1970-01-01
      • 1970-01-01
      • 2014-01-30
      • 2018-03-13
      • 2013-07-01
      相关资源
      最近更新 更多