
Noch vor ein paar Tagen endete in Denver die nĂ€chste, bereits fĂŒnfte Konferenz in Folge, die gröĂte Konferenz auf Go - GopherCon . Daraufhin gab das Go-Team eine wichtige ErklĂ€rung ab - EntwĂŒrfe des vorlĂ€ufigen Entwurfs der neuen Fehlerbehandlung und Generika in Go 2 werden veröffentlicht , und jeder ist zur Diskussion eingeladen.
Ich werde versuchen, das Wesentliche dieser EntwĂŒrfe in drei Artikeln im Detail zu wiederholen.
Wie viele wahrscheinlich wissen, gab das Go-Team im vergangenen Jahr (auch auf der GopherCon) bekannt, dass es Berichte ( Erfahrungsberichte ) und VorschlĂ€ge zur Lösung der Hauptprobleme von Go sammelt - die Punkte, die von Umfragen am meisten kritisiert wurden. Im Laufe des Jahres wurden alle VorschlĂ€ge und Berichte untersucht und geprĂŒft und halfen bei der Erstellung von EntwurfsentwĂŒrfen, die erörtert werden.
Beginnen wir also mit den EntwĂŒrfen des neuen Fehlerbehandlungsmechanismus .
Zu Beginn ein kleiner Exkurs:
- Go 2 ist ein bedingter Name - alle Innovationen werden Teil des normalen Prozesses der Veröffentlichung von Go-Versionen sein. Es ist also noch nicht bekannt, ob es Go 1.34 oder Go2 sein wird. Python 2/3 Skript wird nicht Eisen sein.
- EntwĂŒrfe von Designs sind nicht einmal VorschlĂ€ge , mit denen Ănderungen in der Bibliothek, der Optimierung oder der Go-Sprache beginnen. Dies ist der Ausgangspunkt fĂŒr die vom Go-Team vorgeschlagene Designdiskussion nach mehrjĂ€hriger Arbeit an diesen Themen. Alles, was in den EntwĂŒrfen mit hoher Wahrscheinlichkeit beschrieben wird, wird sich Ă€ndern und im besten Fall erst nach wenigen Veröffentlichungen (ich gebe ~ 2 Jahre) RealitĂ€t.
Was ist das Problem bei der Fehlerbehandlung in Go?
Go entschied sich zunĂ€chst fĂŒr die Verwendung der "expliziten" FehlerprĂŒfung im Gegensatz zur "impliziten" PrĂŒfung, die in anderen Sprachen beliebt ist - Ausnahmen. Das Problem bei der impliziten FehlerprĂŒfung besteht darin, wie sie im Artikel âSauberer, eleganter und nicht korrekterâ ausfĂŒhrlich beschrieben wird. Es ist sehr schwierig, visuell zu verstehen, ob sich das Programm bei bestimmten Fehlern korrekt verhĂ€lt.
Nehmen Sie ein Beispiel fĂŒr ein hypothetisches Go mit Ausnahmen:
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
)
? ?