معالجة الخطأ في Go 2

العنوان


قبل يومين فقط في دنفر ، التالي ، الخامس بالفعل على التوالي ، أكبر مؤتمر على Go - انتهى GopherCon . في ذلك ، أدلى فريق Go ببيان مهم - تم نشر مسودات التصميم الأولي لمعالجة الأخطاء الجديدة والأدوية في Go 2 ، والجميع مدعو للمناقشة.


سأحاول إعادة سرد تفاصيل هذه المسودات بالتفصيل في ثلاث مقالات.


كما يعلم الكثيرون على الأرجح ، في العام الماضي (أيضًا في GopherCon) ، أعلن فريق Go أنه كان يجمع التقارير ( تقارير التجارب ) والاقتراحات لحل مشاكل Go الرئيسية - تلك النقاط التي تعرضت لانتقادات أكثر من قبل الاستطلاعات. تم خلال العام دراسة ودراسة جميع المقترحات والتقارير وساعدت في إنشاء مسودات التصاميم التي سيتم مناقشتها.


لذا ، لنبدأ بمسودات آلية معالجة الأخطاء الجديدة .


للبدء ، انحراف صغير:


  1. Go 2 هو اسم شرطي - ستكون جميع الابتكارات جزءًا من العملية العادية لإصدار إصدارات Go. لذا لا يزال من غير المعروف ما إذا كان سيكون Go 1.34 أو Go2. لن يكون نص Python 2/3 من الحديد.
  2. مسودات التصميم ليست حتى مقترحات ، حيث يبدأ أي تغيير في المكتبة أو الضبط أو لغة 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 ,

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)



? ?


Source: https://habr.com/ru/post/ar422049/


All Articles