我正在编写一个函数,它将输入数据作为字符串,并调用SHA算法的位大小。它应该将结果哈希作为字节切片返回(第一次尝试):
package main
import (
"crypto/sha256"
"crypto/sha512"
"errors"
"fmt"
)
func main() {
input := "This is a test."
sha256, _ := shaSum(input, 256)
sha384, _ := shaSum(input, 384)
sha512, _ := shaSum(input, 512)
fmt.Println(input, sha256, sha384, sha512)
}
func shaSum(data string, size uint) ([]byte, error) {
input := []byte(data)
switch size {
case 256:
return sha256.Sum256(input), nil
case 384:
return sha512.Sum384(input), nil
case 512:
return sha512.Sum512(input), nil
default:
return nil, errors.New("unsupported sha size")
}
}
当然,这不起作用:
$ go run shasum.go
# command-line-arguments
./shasum.go:22:23: cannot use sha256.Sum256(input) (type [32]byte) as type []byte in return argument
./shasum.go:24:23: cannot use sha512.Sum384(input) (type [48]byte) as type []byte in return argument
./shasum.go:26:23: cannot use sha512.Sum512(input) (type [64]byte) as type []byte in return argument
所以我试图从哈希函数的返回值中获取切片,在每次调用(第二次尝试)后添加[:]
:
func shaSum(data string, size uint) ([]byte, error) {
input := []byte(data)
switch size {
case 256:
return sha256.Sum256(input)[:], nil
case 384:
return sha512.Sum384(input)[:], nil
case 512:
return sha512.Sum512(input)[:], nil
default:
return nil, errors.New("unsupported sha size")
}
}
这不起作用:
$ go run shasum.go
# command-line-arguments
./shasum.go:22:30: invalid operation sha256.Sum256(input)[:] (slice of unaddressable value)
./shasum.go:24:30: invalid operation sha512.Sum384(input)[:] (slice of unaddressable value)
./shasum.go:26:30: invalid operation sha512.Sum512(input)[:] (slice of unaddressable value)
所以我试图获取返回值的地址,使用括号确保首先采用表达式的地址然后切片(第三次尝试):
func shaSum(data string, size uint) ([]byte, error) {
input := []byte(data)
switch size {
case 256:
return (&(sha256.Sum256(input)))[:], nil
case 384:
return (&(sha512.Sum384(input)))[:], nil
case 512:
return (&(sha512.Sum512(input)))[:], nil
default:
return nil, errors.New("unsupported sha size")
}
}
产生此错误消息:
$ go run shasum.go
# command-line-arguments
./shasum.go:22:10: cannot take the address of sha256.Sum256(input)
./shasum.go:24:10: cannot take the address of sha512.Sum384(input)
./shasum.go:26:10: cannot take the address of sha512.Sum512(input)
所以我放弃并使用额外的线条进行解决(第四次尝试):
func shaSum(data string, size uint) ([]byte, error) {
input := []byte(data)
switch size {
case 256:
bytes := sha256.Sum256(input)
return bytes[:], nil
case 384:
bytes := sha512.Sum384(input)
return bytes[:], nil
case 512:
bytes := sha512.Sum512(input)
return bytes[:], nil
default:
return nil, errors.New("unsupported sha size")
}
}
最终编译并运行。现在我想知道:为什么第四次尝试起作用而其他人(特别是第三次尝试)不合适?一个好的解决方案怎么样?有没有办法避免像第四次尝试那样的额外线?
编辑:我的问题的根本问题不是如何从字节数组转换为字节切片,而是我对可寻址概念缺乏了解,以及如何表达解决方案我在惯用Go中的问题。
答案 0 :(得分:1)
重要的是,请注意sha256.Sum256
function(和其他SHA函数)的签名会返回数组,这与 slice 完全不同。 。本文明确解释了差异:Go Slices: usage and internals。
为了说明,请考虑以下事项:
x := [3]int{1, 2, 3} // x has type [3]int
y := []int(x) // ERROR: cannot convert x (type [3]int) to type []int
这就是为什么你的每次尝试都不起作用的原因:
shaSum
函数返回([]byte, error)
的元组,但每个return语句都会尝试返回([Size]byte, error)
,因此编译器会抱怨类型不匹配。
每个sha*.Sum*
函数返回一个([Size]byte, error)
元组;你不能在这样的元组上使用[:]
运算符,只能在arrays and slices上使用。
与上面的#2类似,您只能使用addressable types上的&
运算符,该运算符不包含函数调用(或它们返回的值,直到它们存储在本地)。
这个工作示例是惯用的。语言设计者故意选择优化读者的清晰度而不是作者的简写。
答案 1 :(得分:1)
语言规范states this about the slice operator:
如果切片操作数是一个数组,则它必须是可寻址的,切片操作的结果是一个与数组具有相同元素类型的切片。
操作数必须是可寻址的,即,变量,指针间接或切片索引操作;或可寻址结构操作数的字段选择器;或者可寻址数组的数组索引操作。
由此可见,第四次尝试是唯一有效的尝试。
这是使用hash.Hash界面的替代方法。
var hashFactory = map[int]func() hash.Hash{
256: sha256.New,
384: sha512.New384,
512: sha512.New,
}
func shaSum(data string, size int) ([]byte, error) {
f := hashFactory[size]
if f == nil {
return nil, errors.New("unsupported sha size")
}
h := f()
io.WriteString(h, data)
return h.Sum(nil), nil
}