如何解决如何在 Crystal 中进行通用记忆?
我想在 Crystal 中定义一个通用的记忆包装器。 我有以下水晶代码:
module Scalar(T)
abstract def value: T
end
class ScSticky(T)
include Scalar(T)
def initialize(sc : Scalar(T))
@sc = sc
@val = uninitialized T
end
def value: T
@val ||= @sc.value
end
end
换句话说,我希望 ScSticky
只调用底层 Scalar(T)
一次,并为所有后续调用返回缓存的输出。
但是,如果 T
是 Int32
比如包装这个类时
class ScCounter
include Scalar(Int32)
def initialize
@val = 100
end
def value: Int32
@val += 1
@val
end
end
ScSticky(ScCounter.new).value
将始终等于 0
(据我所知,因为 unitialized Int32
实际上初始化为 0 值)
我非常感谢您对此问题的帮助
Upd:似乎实现这一点的正确方法是使用 nil
,但是我在理解这种实现应该是什么样子时遇到了问题。我也希望能够记住 .value
方法,即使它返回 nil
(换句话说,如果 T
是一个 nilable 类型)
解决方法
您正在使用不安全的功能“uninitialized
”,这意味着“保留之前内存中的任何内容”(理论上该值是随机的并且可能无效,实际上您经常以 0 结束 - - 但它仍然无法保证)。
关于 uninitialized
功能的简短故事是请永远不要使用它。
如果你写了 @val = 0
,这种行为不会让你感到惊讶——这就是你写的。
您必须定义 @val : T? = nil
-- 以使其变为 nilable(具有 nil
这个单独的可能值,它是它自己的类型 - Nil
)。
您可能认为 unitialized
会将 nil
带入图片中,但它绝对没有。
为了回应您关于将 nil
也包含到可能值中的评论,这里有一个完整的解决方案,它使用用户永远无法创建的独特“哨兵”结构而不是 Nil。
module Scalar(T)
abstract def value: T
end
private struct Sentinel
end
class ScSticky(T)
include Scalar(T)
@val : T | Sentinel = Sentinel.new
def initialize(@sc : Scalar(T))
end
def value: T
val = @val
if val.is_a?(Sentinel)
@val = @sc.value
else
val
end
end
end
class ScCounter
include Scalar(Int32)
def initialize
@val = 100
end
def value: Int32
@val += 1
end
end
sc = ScSticky.new(ScCounter.new)
p! sc.value #=> 101
p! sc.value #=> 101
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。