首页 > 解决方案 > 在这种情况下,我如何处理好 nil ?

问题描述

情况

我写了以下代码。

class Gear
  getter :chainring, :cog, :wheel

  def initialize(@chainring : Int32, @cog : Int32, @wheel : Wheel?)
  end

  def ratio
    chainring / cog.to_f
  end

  def gear_inches
    ratio * wheel.diameter
  end
end

然后出现如下编译错误

$ crystal run  gear.cr 
Showing last frame. Use --error-trace for full trace.

In bicycle.cr:12:19

 12 | ratio * wheel.diameter

我想做的事情

我想在不使用try方法的情况下修复此编译错误。

我尝试了什么,我研究了什么

  1. Crystal 语言不允许使用安全导航算子。
  2. 仅当 wheel 变量为真时才执行该diameter方法,如在 TypeScript 中。示例如下。
def gear_inches
  if wheel
    ratio * wheel.diameter
  else
    0
  end
end

就是这样的问题。我会很感激你能给我的任何建议。

标签: crystal-lang

解决方案


In a concurrent program the value of an instance variable could change between any two accesses to it. getter wheel makes wheel just return the instance variable @wheel. So between checking if wheel and the second call in wheel.diameter Crystal cannot tell that the value couldn't have changed.

The usual solution to this is to assign the value to a local variable, which allows Crystal to reason about when the value cannot change:

record Wheel, diameter : Int32

class Gear
  getter :chainring, :cog, :wheel

  def initialize(@chainring : Int32, @cog : Int32, @wheel : Wheel?)
  end

  def ratio
    chainring / cog.to_f
  end

  def gear_inches
    (wheel = self.wheel) ? ratio * wheel.diameter : 0
  end
end

gear = Gear.new(1, 2, Wheel.new(3))
puts gear.gear_inches

In many situations a nice alternative is to see if you can find a default value to internally return for your optional field:

record Wheel, diameter : Int32

class Gear
  getter :chainring, :cog

  def initialize(@chainring : Int32, @cog : Int32, @wheel : Wheel?)
  end

  def ratio
    chainring / cog.to_f
  end

  def gear_inches
    ratio * wheel.diameter
  end
  
  private def wheel
    @wheel || Wheel.new(0)
  end
end

gear = Gear.new(1, 2, nil)
puts gear.gear_inches

推荐阅读