如何在不收到此错误消息的情况下将哈希转换为别名类型?
How do I cast a Hash as an aliased type without getting this error message?
当我使用 alias
定义哈希时,我遇到了一个有趣的错误:
alias MyHash = Hash(Symbol, String | Int32)
hash = {:one => 2}.as(MyHash)
如果我 运行 这输出不是我预期的:
Error in ffile2.cr:2: can't cast Hash(Symbol, Int32) to Hash(Symbol, Int32 | String)
hash = {:one => 2}.as(MyHash)
^
你不能?为什么不?这不是定义统一类型的目的吗?
请注意,如果我将类型放在方法签名中,一切都很好:
def foo(h : Hash(Symbol, String | Int32) )
puts h[:bar]?.nil?
end
foo( {:one => 2} )
更新:这行得通,但看起来有点傻:
alias MyHash = Hash(Symbol, String | Int32)
hash = {:one => 2.as(String | Int32)}.as(MyHash)
这与别名无关。如果您将原始示例中的别名替换为别名类型,它也会失败。
.as
无法神奇地将 Hash(Symbol, Int32)
转换为 Hash(Symbol, String | Int32)
。这些是不同的类型并且表现不同。一开始这可能并不明显,因为在检索条目时,两种类型都 return 类型匹配 String | Int32
的值。存储条目时,它们的行为不同:Hash(Symbol, String | Int32)
可以接收类型为 String | Int32
的值,Hash(Symbol, Int32)
不能。
这方面的语言设计术语是协变和逆变。
为避免必须在文字中指定预期值类型,您还可以使用泛型转换,例如:
literal = {:one => 2}
mapped = literal.map do |key, value|
{key, value.as(String | Int32)}
end.to_h
typeof(mapped) # => Hash(Symbol, Int32 | String)
这将采用匹配类型的任何散列并将其转换为 Hash(Symbol, Int32 | String)
。
方法参数中的类型不是 "fine"它们只是表现不同。只要您没有对它们做任何错误,它们就会匹配未指定的类型。尝试给字符串赋值,还是会失败:
def foo(h : Hash(Symbol, String | Int32) )
puts h[:bar] = "bar" # error: no overload matches 'Hash(Symbol, Int32)#[]=' with types Symbol, String
end
这些不同的语义显然不是很直观,但这是正在进行的工作的当前状态。有一个问题也更详细地解释了这是关于什么的:https://github.com/crystal-lang/crystal/issues/3803
当我使用 alias
定义哈希时,我遇到了一个有趣的错误:
alias MyHash = Hash(Symbol, String | Int32)
hash = {:one => 2}.as(MyHash)
如果我 运行 这输出不是我预期的:
Error in ffile2.cr:2: can't cast Hash(Symbol, Int32) to Hash(Symbol, Int32 | String)
hash = {:one => 2}.as(MyHash)
^
你不能?为什么不?这不是定义统一类型的目的吗?
请注意,如果我将类型放在方法签名中,一切都很好:
def foo(h : Hash(Symbol, String | Int32) )
puts h[:bar]?.nil?
end
foo( {:one => 2} )
更新:这行得通,但看起来有点傻:
alias MyHash = Hash(Symbol, String | Int32)
hash = {:one => 2.as(String | Int32)}.as(MyHash)
这与别名无关。如果您将原始示例中的别名替换为别名类型,它也会失败。
.as
无法神奇地将 Hash(Symbol, Int32)
转换为 Hash(Symbol, String | Int32)
。这些是不同的类型并且表现不同。一开始这可能并不明显,因为在检索条目时,两种类型都 return 类型匹配 String | Int32
的值。存储条目时,它们的行为不同:Hash(Symbol, String | Int32)
可以接收类型为 String | Int32
的值,Hash(Symbol, Int32)
不能。
这方面的语言设计术语是协变和逆变。
为避免必须在文字中指定预期值类型,您还可以使用泛型转换,例如:
literal = {:one => 2}
mapped = literal.map do |key, value|
{key, value.as(String | Int32)}
end.to_h
typeof(mapped) # => Hash(Symbol, Int32 | String)
这将采用匹配类型的任何散列并将其转换为 Hash(Symbol, Int32 | String)
。
方法参数中的类型不是 "fine"它们只是表现不同。只要您没有对它们做任何错误,它们就会匹配未指定的类型。尝试给字符串赋值,还是会失败:
def foo(h : Hash(Symbol, String | Int32) )
puts h[:bar] = "bar" # error: no overload matches 'Hash(Symbol, Int32)#[]=' with types Symbol, String
end
这些不同的语义显然不是很直观,但这是正在进行的工作的当前状态。有一个问题也更详细地解释了这是关于什么的:https://github.com/crystal-lang/crystal/issues/3803