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

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

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

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 语言必须解决的问题。

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

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

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

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

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

考虑一下:

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

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