
就在几天前,在丹佛举行的下一届Go- GopherCon大会已经连续第五次结束了 。 Go团队在会议上发表了重要声明 - 发布了Go 2中新错误处理和泛型的初步设计草案,并邀请所有人进行讨论。
我将尝试在三篇文章中详细讲述这些草案的实质。
正如许多人可能知道的那样,去年(也是在GopherCon上),Go团队宣布正在收集报告( 经验报告 )和解决Go的主要问题的建议-这些观点在民意调查中最受批评。 在这一年中,所有提案和报告都得到了研究和考虑,并帮助创建了设计草案,将进行讨论。
因此,让我们从新的 错误处理机制的草案开始。
首先,进行一点题外话:
- Go 2是有条件的名称-所有创新都将成为发布Go版本的正常过程的一部分。 因此,仍然是Go 1.34还是Go2仍是未知的。 Python 2/3脚本不会很强大。
- 设计草案甚至都不是提案 ,库,调整或Go语言的任何更改都从提案开始。 这是Go团队在这些问题上工作了几年之后提出的设计讨论的起点。 草案中描述的所有可能性很高的事物都将被更改,并且在最佳情况下,只有在发布了几次后(我给出了〜2年),才可能成为现实。
Go中的错误处理有什么问题?
Go最初决定使用“显式”错误检查,而不是其他语言中流行的“隐式”检查-异常。 隐式错误检查的问题是如何在文章“更干净,更优雅,更不正确”中对其进行详细描述的,这在视觉上很难理解在某些错误情况下程序是否正确运行。
举一个假设Go的例子,但有例外:
func CopyFile(src, dst string) throws error {
r := os.Open(src)
defer r.Close()
w := os.Create(dst)
io.Copy(w, r)
w.Close()
}
, . : io.Copy
w.Close
, .
, Go :
func CopyFile(src, dst string) error {
r, err := os.Open(src)
if err != nil {
return err
}
defer r.Close()
w, err := os.Create(dst)
if err != nil {
return err
}
defer w.Close()
if _, err := io.Copy(w, r); err != nil {
return err
}
if err := w.Close(); err != nil {
return err
}
}
, , , – . , , – " ", - , , , .
, ( , , ..) , .
, Go . :
func CopyFile(src, dst string) error {
r, err := os.Open(src)
if err != nil {
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
defer r.Close()
w, err := os.Create(dst)
if err != nil {
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
if _, err := io.Copy(w, r); err != nil {
w.Close()
os.Remove(dst)
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
if err := w.Close(); err != nil {
os.Remove(dst)
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
}
, .
Go Go 2:
Go.
.
check(x,y,z)
check err
handle
– ,
check
, handle
( handler
, , . , return
)
:
func CopyFile(src, dst string) error {
handle err {
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
r := check os.Open(src)
defer r.Close()
w := check os.Create(dst)
handle err {
w.Close()
os.Remove(dst) // ( check )
}
check io.Copy(w, r)
check w.Close()
return nil
}
, ( main
). :
func main() {
hex, err := ioutil.ReadAll(os.Stdin)
if err != nil {
log.Fatal(err)
}
data, err := parseHexdump(string(hex))
if err != nil {
log.Fatal(err)
}
os.Stdout.Write(data)
}
:
func main() {
handle err {
log.Fatal(err)
}
hex := check ioutil.ReadAll(os.Stdin)
data := check parseHexdump(string(hex))
os.Stdout.Write(data)
}
, . :
func printSum(a, b string) error {
x, err := strconv.Atoi(a)
if err != nil {
return err
}
y, err := strconv.Atoi(b)
if err != nil {
return err
}
fmt.Println("result:", x + y)
return nil
}
:
func printSum(a, b string) error {
handle err { return err }
x := check strconv.Atoi(a)
y := check strconv.Atoi(b)
fmt.Println("result:", x + y)
return nil
}
:
func printSum(a, b string) error {
handle err { return err }
fmt.Println("result:", check strconv.Atoi(x) + check strconv.Atoi(y))
return nil
}
check
handle
.
Check
check
( ) , "" error
, , . nil, check
(handler
), return
.
:
v1, ..., vN := check <>
:
v1, ..., vN, vErr := <>
if vErr != nil {
<error result> = handlerChain(vn)
return
}
vErr
error
<error result>
, .
,
foo(check <>)
:
v1, ..., vN, vErr := <>
if vErr != nil {
<error result> = handlerChain(vn)
return
}
foo(v1, ..., vN)
Check try
try
check
– /, , , Rust Swift try
( Rust ?
).
try
:
data := try parseHexdump(string(hex))
:
data, err := parseHexdump(string(hex))
if err == ErrBadHex {
... special handling ...
}
try err
, try
c . check
/handle
, check
.
Handle
handle
, "" (handler), , check
. (return) . ( , return
) ( func foo() (bar int, err error)
).
, " " – , , , error
, , . :
func handler(err error) error {...}
( , , – ).
– , . (check
) , , . , , – . :
func process(user string, files chan string) (n int, err error) {
handle err { return 0, fmt.Errorf("process: %v", err) } // handler A
for i := 0; i < 3; i++ {
handle err { err = fmt.Errorf("attempt %d: %v", i, err) } // handler B
handle err { err = moreWrapping(err) } // handler C
check do(something()) // check 1: handler chain C, B, A
}
check do(somethingElse()) // check 2: handler chain A
}
check 1
C, B A – . check 2
A, C B for-.
, . if
, (handle
) ( ) , – , , :
type Error struct {
Func string
User string
Path string
Err error
}
func (e *Error) Error() string
func ProcessFiles(user string, files chan string) error {
e := Error{ Func: "ProcessFile", User: user}
handle err { e.Err = err; return &e } // handler A
u := check OpenUserInfo(user) // check 1
defer u.Close()
for file := range files {
handle err { e.Path = file } // handler B
check process(check os.Open(file)) // check 2
}
...
}
, handle
defer
, , , . – , . , handler B
– defer
, . Go defer
/panic
handle
/check
, , -.
– (.. return
), - . .
(panic) , .
-
– (handle err {}
). " -" (default handler). handle
, , -, , check
( ; — zero values).
-:
func printSum(a, b string) error {
x := check strconv.Atoi(a)
y := check strconv.Atoi(b)
fmt.Println("result:", x + y)
return nil
}
Go , . - , , . , t.Helper()
, :
func TestFoo(t *testing.T) {
handle err {
t.Helper()
t.Fatal(err)
}
for _, tc := range testCases {
x := check Foo(tc.a)
y := check Foo(tc.b)
if x != y {
t.Errorf("Foo(%v) != Foo(%v)", tc.a, tc.b)
}
}
}
(shadowing)
check
(:=
), err
. handle
/check
.
defer/panic
(defer
/panic
handle
/check
) . .
handle
defer
(, , , ), handle
/check
defer-. :
func Greet(w io.WriteCloser) error {
defer func() {
check w.Close()
}()
fmt.Fprintf(w, "hello, world\n")
return nil
}
, .
Go – , . - "", , defer
, break
goto
. , goto
, , .
try
, catch
, ?
, . , Go , check
handle
.
, handle
catch
, ( , (keywords) ).
Go2?
. Go, 2-3 , – . , 2-3 .
, , Go2 – . , Go – Go 1.20 . .
, ?
. / . , , , Go.
Go 2 – , if err != nil {}
, handle
/check
?
, , if err
, – , . , .
? , Go .
. , .
?
, , . , - . , , , , .
, ! ?
- Go2ErrorHandlingFeedback
- Go —
handle
/check
- -
- , ,
- / (
defer
/panic
)
? ?