我有一个如下定义的辅助类:
require "toml"
module Test
class Utils
@@config
def self.config
if @@config.is_a?(Nil)
raw_config = File.read("/usr/local/test/config.toml")
@@config = TOML.parse(raw_config)
end
@@config
end
end
end
当我在代码中的其他地方调用此方法时:
server = TCPServer.new("localhost", Utils.config["port"])
我收到以下编译时错误:
in src/test/daemon.cr:10: undefined method '[]' for Nil (compile-time type is (Hash(String, TOML::Type) | Nil))
server = TCPServer.new("localhost", Utils.config["port"])
Utils.config
无法运行 Nil
,所以我不明白错误 .
-
如何告诉编译器
Utils.config
将始终返回不是Nil
的内容? -
(轻微的附加问题)这是一个很好的设计模式,适用于将在类之间共享的资源(
config
),但只应创建一次?
2 回答
编辑:请参阅下面的JohannesMüller答案,这是一个更好的解决方案 .
通常,如果要避免使用
Nil
,则应键入类和实例变量:@@config : Hash(String,Toml::Type)
这将有助于编译器帮助您 - 通过查找可能导致
Nil
值的代码路径并在编译期间提醒您 .代码的潜在修复:
由于toml要求,我无法直接测试,但是使用字符串的运行示例在这里:https://play.crystal-lang.org/#/r/30kl
对于第二个问题,这种方法可能有效:
使用字符串而不是TOML的示例代码:https://play.crystal-lang.org/#/r/30kt
您的代码的问题在于,在if分支中检查
@config
是否为nil(这对于@config.nil?
更容易),该实例变量的值可能已更改,直到它到达返回行 . 如果编译器从不同的光纤改变,它必须假设它可以再次为零 .您可以将其保存到本地变量并返回此值
或者有点重构,但基本上是相同的事情:
我更喜欢在默认初始化时使用空对象将
@@config
设置为nil
,因为它清楚地表明此对象不可用 . 例如,如果配置文件恰好为空,则检查empty?
将始终触发重新加载和解析,从而消除了memoization功能 .||=
运算符基本上意味着