如何在Crystal中进行通用存储

  • 本文关键字:存储 Crystal crystal-lang
  • 更新时间 :
  • 英文 :


我想在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)一次,并为所有后续调用返回缓存的输出。然而,如果TInt32,则上述方法不起作用

例如,包装此类时


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是可幂类型(

您正在使用不安全的功能"CCD_ 12";,这意味着;保留之前记忆中的任何内容";(理论上,这个值是随机的,可能是无效的,但在实践中,你通常最终会得到0——但它仍然不能保证(
关于uninitialized功能的短篇故事是请永远不要使用它

如果你写了@val = 0,这种行为不会让你感到惊讶——你就是这么写的。

您必须定义@val : T? = nil-以使其为幂零(具有单独的可能值nil,它是自己的类型-Nil(
您可能认为unitializednil带入画面,但事实并非如此。


为了回应您关于将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

相关内容

  • 没有找到相关文章

最新更新