我最近开始学习Rust,我真的很喜欢Result
Enum和?
运营商的实用程序和简洁。因此,我试图看看我能在旅途中实现类似的东西有多近。此外,当某人说“哦,错误处理如此冗长”时,我可以指出这一点。
如果您不熟悉Rust,则koude0类型是可以容纳Ok
有效值或错误的枚举。这很整洁,但真正的实用程序来自?
操作员。当?
在Result
上调用?
运算符时,如果Result
中存在错误,并且只需从父函数返回Result
,则将停止执行代码。基本上,Rust仅用一个字符代替了以下众所周知的Go错误处理样板!
if err != nil {
return nil, err
}
如果Result
中没有错误,则操作员将拆开Ok
值并返回。
Rust和Go是完全不同的,因此从GO中实现相同的Result
Enum和?
操作员并不像移植或翻译代码那样简单。这是我遇到的一些挑战,以及我如何解决它们。
枚举
我认为Go的枚举可能是限制的,并且不像Rust的表现力是很热门的。因此,而不是在Result
类型的go中使用枚举,而不是使用generics和struct。
type Result[T any] struct {
Ok T
Err error
}
问号操作员
这是一个更大的问题。据我所知,GO的功能与此相似。打破“自愿”函数流动流的唯一方法是惊慌。因此,我决定可以在Result
结构上添加一种方法,如果存在错误,该方法会感到恐慌。但这还不是全部,我仍然需要某种方法来恢复此恐慌,并让错误“弹起”呼叫堆栈。实现此目的的方法是将命名的Result
从父函数返回,将指针传递给该名称的Result
到递延功能,该功能将从恐慌中恢复,并将错误放在该指针中。我们称此延期函数EscapeHatch
。这就是外观:
func EscapeHatch[T any](res *Result[T]) {
if r := recover(); r != nil {
err, ok := r.(ehError)
if !ok {
// Panicking again because the recovered panic is not an ehError
panic(r)
}
*res = Result[T]{Err: err.error}
}
}
这里还有一件事。也就是说,我们通过?
操作员引起的任何恐慌都包裹在一个简单的结构中,以便我们仅恢复这些恐慌,并再次为所有其他结构恢复。最后,因为?
不是Go中的方法的有效名称,而是使用Eh
。这对于EscapeHatch
来说是缩写,也是加拿大人在句子结尾处添加的东西,以使其成为问题。例如:“我们的天气很愉快,是吗?”。因此,我认为这是?
操作员的适当替代。
演示时间!
将所有内容整合在一起,我可以像这样转换代码:
func example(aFile string) ([]byte, error) {
f, err := os.Open(aFile)
if err != nil {
return nil, err
}
buff := make([]byte, 5)
_, err := f.Read(b1)
if err != nil {
return nil, err
}
return buff, nil
进入这个:
func example(aFile string) (res eh.Result[[]byte]) {
defer eh.EscapeHatch(&res) // must have pointer to the named output Result
buff := make([]byte, 5)
file := eh.NewResult(os.Open(aFile)).Eh()
_ = eh.NewResult(file.Read(buff)).Eh()
return eh.Result[[]byte]{Ok: buff}
这不是真的骇人听闻吗?
最初,我对这个想法感到更糟,尤其是在这种方式上可能“滥用”恐慌。但是后来我发现,即使是标准GO库中的json package也会使用恐慌,包裹错误并在递归编码接口时恢复错误以停止执行。因此,如果这是一个反模式,那么至少我可以说标准库也在这样做。
无耻库插头
如果您很好奇,我将所有代码和一些实用程序功能组合在一个小包装here中。尝试一下,让我知道您的想法。