preface

Hello, everyone. My name is Asong. Today we are going to talk about “throw, try….. “in go language Catch {} “. If you’re a Java programmer, I’m sure you’ve joked about the go language’s error handling, but this post is not about the good and the bad. The focus of this article is to take you through the implementation of Panic and Recover. In the last article, we explained how to implement defer, but we did not explain Recover, which is closely related to defer. In order to understand the implementation of panic and Recover, it is not so easy. Without further ado, just turn it on.

What is thepanic,recover

The panic keyword in Go language is mainly used to actively throw exceptions, similar to the throw keyword in Java and other languages. Panic can change the control flow of your program by immediately stopping the rest of the current function and recursively executing the caller’s defer in the current Goroutine.

Go language recover keyword is mainly used to catch exceptions, let the program back to the normal state, similar to Java languages such as try… The catch. Recover can stop a program crash caused by Panic. It is a function that only works in defer, not in other scopes;

Recover can only be used in defer. This is already written in the comments of the standard library, so we can have a look:

// The recover built-in function allows a program to manage behavior of a
// panicking goroutine. Executing a call to recover inside a deferred
// function (but not any function called by it) stops the panicking sequence
// by restoring normal execution and retrieves the error value passed to the
// call of panic. If recover is called outside the deferred function it will
// not stop a panicking sequence. In this case, or when the goroutine is not
// panicking, or if the argument supplied to panic was nil, recover returns
// nil. Thus the return value from recover reports whether the goroutine is
// panicking.
func recover(a) interface{}
Copy the code

One important thing to note here is that recover must be used in the defer function, otherwise panic cannot be prevented. The best way to verify this is to write two examples first:

func main(a)  {
	example1()
	example2()
}

func example1(a)  {
	defer func(a) {
		if err := recover(a); err ! =nil{
			fmt.Println(string(Stack()))
		}
	}()
	panic("unknown")}func example2(a)  {
	defer recover(a)panic("unknown")}func Stack(a) []byte {
	buf := make([]byte.1024)
	for {
		n := runtime.Stack(buf, false)
		if n < len(buf) {
			return buf[:n]
		}
		buf = make([]byte.2*len(buf))
	}
}
Copy the code

Panic in the example2() method was not recovered, causing the program to crash. I’m sure you’re wondering why I can’t stop panic by just saying recover(). We explained the implementation mechanism of defer in detail (attached are three interview questions, I don’t believe you can all do it correctly). One important point is that when **defer put the statement on the stack, it will also copy the relevant values. ** So defer recover() was already executed when it was put into the defer stack, and panic happened later, so there was nothing to stop it.

features

Above, we briefly introduced what panic and Recover are. Next, I take a look at their features to avoid us stepping on pits.

  • recoverOnly in thedeferFunction only valid, the above has been illustrated with examples, here is not repeated.
  • panicAllows for thedeferNested multiple calls in. Program multiple callspanicIt doesn’t affectdeferFunction normal execution, so usedeferIt is generally safe to put the finishing touches on. To verify this, write an example:
func example3(a)  {
	defer fmt.Println("this is a example3 for defer use panic")
	defer func(a) {
		defer func(a) {
			panic("panic defer 2")
		}()
		panic("panic defer 1")
	}()
	panic("panic example3")}// Run the result
this is a example3 for defer use panic
panic: panic example3
        panic: panic defer 1
        panic: panic defer 2. omitCopy the code

As you can see from the results of the run, panic does not affect the use of the defer function, so it is safe.

  • panicOnly for the presentGoroutinethedeferIt works. Remember what we did in the last articledeferprocFunction? innewdeferThe distribution of_deferWhen a structure object is created, the assigned object is chained to the currentgoroutinethe_deferThe header of the linked list, where the deferred calling function is placed with the callerGoroutineAssociation. So when the program happenspanicDelayed-calling functions that only call the current Goroutine are fine. To verify this, write an example:
func main(a)  {
	go example4()
	go example5()
	time.Sleep(10 * time.Second)
}

func example4(a)  {
	fmt.Println("goroutine example4")
	defer func(a) {
		fmt.Println("test defer")
	}()
	panic("unknown")}func example5(a)  {

	defer fmt.Println("goroutine example5")
	time.Sleep(5 * time.Second)
}
// Run the result
goroutine example4
test defer
panic: unknown ............. Omit some codeCopy the code

Here I have two coroutines, one of which will panic and cause the program to crash, but it will only execute the delay function of its own Goroutine, so it just validates that there is not much correlation between multiple Goroutines. One Goroutine should not perform the delay functions of other Goroutines during panic.

Typical applications

In fact, we often encounter panic problems in the actual project development, Go runtime code in many places called panic function, for the new people do not understand the underlying implementation of Go, this is undoubtedly a pile of deep holes. We always have panic in the actual production environment, but our program still runs normally, this is because our framework has done RECOVER, it has covered for us, such as gin, let’s take a look at how he does it.

Let’s start with the code:

func Default(a) *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}
// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
func Recovery(a) HandlerFunc {
	return RecoveryWithWriter(DefaultErrorWriter)
}

// RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one.
func RecoveryWithWriter(out io.Writer) HandlerFunc {
	var logger *log.Logger
	ifout ! =nil {
		logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags)
	}
	return func(c *Context) {
		defer func(a) {
			if err := recover(a); err ! =nil {
				// Check for a broken connection, as it is not really a
				// condition that warrants a panic stack trace../ / to omit
			}
		}()
		c.Next()
	}
}
Copy the code

When using GIN, the first step is to initialize an Engine instance and attach recovery Middleware by calling the Default method. Recovery uses the defer function to prevent panic by recovering. 500 error codes are returned. It should be noted that only panic in the main program will be recovered automatically. Panic in the coroutine will cause the whole program to crash. A coroutine can panic, causing the program to crash, but it will only execute the delay function of its own Goroutine, so it can verify that there is not much correlation between multiple Goroutines. One Goroutine should not perform the delay functions of other Goroutines during panic. For robustness, we should actively check our coroutines ourselves. It is necessary to add RECOVER to our coroutine functions, for example:

func main(a)  {
		r := gin.Default()
		r.GET("/asong/test/go-panic".func(ctx *gin.Context) {
			go func(a) {
				defer func(a) {
					if err := recover(a); err ! =nil{
						fmt.Println(err)
					}
				}()
				panic("panic")
			}()
		})
		r.Run()
}
Copy the code

If you are using Gin frameworks, be sure to check for panic in coroutines, otherwise you will pay a heavy price on the line. Very dangerous!!

The source code parsing

Go – version: 1.15.3

Let’s start with a simple code that looks at its assembly call:

func main(a)  {
	defer func(a) {
		if err:= recover(a); err ! =nil{
			fmt.Println(err)
		}
	}()
	panic("unknown")}Copy the code

Run go tool compile-n -l -s main.go to see the corresponding assembly code, we intercept part of the fragment analysis:

The first step is to call Runtime.DeferprocStack to create the defer object. This may be confusing, but I forgot to mention it in the last article. Here’s a summary: There are three models of defer, and only one of them can be compiled in a function

  • The first, deferproc, basically relies on the runtime to allocate “_defer” objects and add deferred parameters. Insert at the end of the functiondeferreturnMethods to consumedeferThe link.
  • The second, deferprocStack, is basically the same as the heap, except that it is allocated on the stack instead, pushing into the stack of function calls_deferRecord the compiler inssaThere will be reservation in the processdeferSpace.
  • The third, Open Coded, is conditional. By default, open-coded supports a maximum of 8 defer, and will be cancelled if more than that. Open-coded mode is not applicable when building SSA if N parameters are not optimized in GcFlags or the number of returns * defer exceeds 15. You can’t be in a loop.

Our version is 1.15+, so we should be using open encoding mode, but why is it still allocated on the stack? Note, guys, that I disabled compilation optimizations in assembly processing, which is definitely not open code mode. That’s not the point. Let’s move on to the above assembly.

The second red line is to call Runtime. gopanic when a panic occurs. Now that the program is panic, runtime.deferreturn is called when the function returns. This step is the main program execution part, let’s look at the execution of the delay function:

The most important thing here is to call Runtime. gorecover, which is in this step, the main program panic recovery, this is the implementation process of panic and recover. Let’s look at how runtime.gopanic and Runtime. gorecover are implemented.

_panic structure

When we talked about the implementation mechanism of defer, we looked at the structure of defer. One of the fields was _panic, which triggered defer. Let’s take a look at the structure of panic:

type _panic struct {
	argp      unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink
	arg       interface{}    // argument to panic
	link      *_panic        // link to earlier panic
	pc        uintptr        // where to return to in runtime if this panic is bypassed
	sp        unsafe.Pointer // where to return to in runtime if this panic is bypassed
	recovered bool           // whether this panic is over
	aborted   bool           // the panic was aborted
	goexit    bool
}
Copy the code

A brief description of the fields above:

  • argpIs pointing todeferA pointer to the argument when called.
  • argIs that we callpanicIs passed as a parameter
  • linkThe point is to call earlierruntime._panicStructure, that is to saypaincCan be called consecutively, they form a linked list
  • recoveredSaid that the currentruntime._panicWhether berecoverrestore
  • abortedRepresents the currentpanicWhether it was forcibly terminated

There is a goexit method in the Runtime package. Goext can terminate the goroutine that calls it. Other goroutines are unaffected. Goexit is not a panic, so any of the recover calls in these delay functions will return nil. If we call Goexit in the main function it terminates the goroutine but does not return func main. Since func main does not return, the program continues with the other Gorountine until all other Goroutines exit and the program crashes. Here’s a simple example:

func main(a)  {
	go func(a) {
		defer func(a) {
			if err := recover(a); err ! =nil {
				fmt.Println(err)
			}
		}()
		runtime.Goexit()
	}()
	go func(a) {
		for true {
			fmt.Println("test")
		}
	}()
	runtime.Goexit()
	fmt.Println("main")
	select{}}Copy the code

Run the above example and you’ll see that even if runtime.Goexit is called in the main Goroutine, the other Goroutines have no effect. Therefore, the three fields PC, sp and goexit in the structure are for repairing runtime. goexit. These three fields are to ensure that the function will take effect, because if panic occurs in defer, the goexit function will be cancelled, so these three fields are protected. Look at this example:

func main(a)  {
	maybeGoexit()
}
func maybeGoexit(a) {
	defer func(a) {
		fmt.Println(recover() ()}defer panic("cancelled Goexit!")
	runtime.Goexit()
}
Copy the code

Good English can take a look at this: github.com/golang/go/i…

Let’s get to the point.

gopanic

Gopanic’s code is a bit long, so let’s break it down bit by bit:

  • The first part, judgmentpanicType:
gp := getg()
	ifgp.m.curg ! = gp {print("panic: ")
		printany(e)
		print("\n")
		throw("panic on system stack")}ifgp.m.mallocing ! =0 {
		print("panic: ")
		printany(e)
		print("\n")
		throw("panic during malloc")}ifgp.m.preemptoff ! ="" {
		print("panic: ")
		printany(e)
		print("\n")
		print("preempt off reason: ")
		print(gp.m.preemptoff)
		print("\n")
		throw("panic during preemptoff")}ifgp.m.locks ! =0 {
		print("panic: ")
		printany(e)
		print("\n")
		throw("panic holding locks")}Copy the code

There is nothing more to say here, but let’s move on.

  • The second part is to make sure that eachrecoverAre trying to restore the most recent generation of the current coroutine that has not yet been recoveredpanic
var p _panic // Declare a panic structure
	p.arg = e // Assign the value passed by panic to 'arg'
	p.link = gp._panic // Points to the runtime.panic structure
	gp._panic = (*_panic)(noescape(unsafe.Pointer(&p)))

	atomic.Xadd(&runningPanicDefers, 1)

	// By calculating getcallerpc/getcallersp here, we avoid scanning the
	// gopanic frame (stack scanning is slow...)
	addOneOpenDeferFrame(gp, getcallerpc(), unsafe.Pointer(getcallersp()))

	for {
		d := gp._defer // Get the current Gorourine defer
		if d == nil {
			break // If there is no defer, just exit
		}

		// If defer was started by earlier panic or Goexit (and, since we're back here, that triggered a new panic),
		// take defer off list. An earlier panic will not continue running, but we will make sure below that an
		// earlier Goexit does continue running.
		if d.started {
			ifd._panic ! =nil {
				d._panic.aborted = true
			}
			d._panic = nil
			if! d.openDefer {// For open-coded defers, we need to process the
				// defer again, in case there are any other defers
				// to call in the frame (not including the defer
				// call that caused the panic).
				d.fn = nil
				gp._defer = d.link
				freedefer(d)
				continue}}// Mark defer as started, but keep on list, so that traceback
		// can find and update the defer's argument frame if stack growth
		// or a garbage collection happens before reflectcall starts executing d.fn.
		d.started = true
    // Record the panic that is running the defer.
		// If there is a new panic during the deferred call, that panic
		// will find d in the list and will mark d._panic (this panic) aborted.
		d._panic = (*_panic)(noescape(unsafe.Pointer(&p)))
Copy the code

Instead of explaining the hard parts of the code above, I have added comments to go straight to the D.started section, which means that if defer was started by a previous Panic or Goexit (the processing loops back here, which triggered a new panic), remove defer from the list. The earlier panic will not continue, but we will make sure that the earlier Goexit will continue, and the code in the if D. _panic! Aborted =true} to ensure that the previous panic is aborted, set aborted to true, and that goexit will not be cancelled when recover is executed below.

  • Part three,deferInline optimizes call performance
	if! d.openDefer {// For open-coded defers, we need to process the
				// defer again, in case there are any other defers
				// to call in the frame (not including the defer
				// call that caused the panic).
				d.fn = nil
				gp._defer = d.link
				freedefer(d)
				continue
			}

		done := true
		if d.openDefer {
			done = runOpenDeferFrame(gp, d)
			ifdone && ! d._panic.recovered { addOneOpenDeferFrame(gp,0.nil)}}else {
			p.argp = unsafe.Pointer(getargp(0))
			reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
		}
Copy the code

The above code is all screenshot fragments. These parts are to judge whether the current defer can use the development coding mode. The specific operation will not be expanded.

  • Part four,gopanicTo perform program recovery

In the third part of the defer inline optimization selection, the deferred function (reflectcall for this purpose) is called, namely runtime.gorecover with recoverd = true. The operation of this function will be left below. Because the runtime. goRecover function does not contain the logic for the recovery program, the recovery of the program is performed in Gopanic. Take a look at the code:

		if p.recovered { // Set it to true in runtime.gorecover
			gp._panic = p.link 
			ifgp._panic ! =nil && gp._panic.goexit && gp._panic.aborted { 
				// A normal recover would bypass/abort the Goexit. Instead,
				// we return to the processing loop of the Goexit.
				gp.sigcode0 = uintptr(gp._panic.sp)
				gp.sigcode1 = uintptr(gp._panic.pc)
				mcall(recovery)
				throw("bypassed recovery failed") // mcall should not return
			}
			atomic.Xadd(&runningPanicDefers, - 1)

			if done {
				// Remove any remaining non-started, open-coded
				// defer entries after a recover, since the
				// corresponding defers will be executed normally
				// (inline). Any such entry will become stale once
				// we run the corresponding defers inline and exit
				// the associated stack frame.
				d := gp._defer
				var prev *_defer
				ford ! =nil {
					if d.openDefer {
						if d.started {
							// This defer is started but we
							// are in the middle of a
							// defer-panic-recover inside of
							// it, so don't remove it or any
							// further defer entries
							break
						}
						if prev == nil {
							gp._defer = d.link
						} else {
							prev.link = d.link
						}
						newd := d.link
						freedefer(d)
						d = newd
					} else {
						prev = d
						d = d.link
					}
				}
			}

			gp._panic = p.link
			// Aborted panics are marked but remain on the g.panic list.
			// Remove them from the list.
			forgp._panic ! =nil && gp._panic.aborted {
				gp._panic = gp._panic.link
			}
			if gp._panic == nil { // must be done with signal
				gp.sig = 0
			}
			// Pass information about recovering frame to recovery.
			gp.sigcode0 = uintptr(sp)
			gp.sigcode1 = pc
			mcall(recovery)
			throw("recovery failed") // mcall should not return
		}
Copy the code

This code is a bit long, but it is mainly divided into two parts:

The first part focuses on this judgment if Gp._panic! = nil && gp._panic.goexit && gp._panic.aborted { … }, normal recover will bypass Goexit, so to solve this problem, add this judgment, so that Goexit is also guaranteed to recover, This is done by taking the program counter PC and the stack pointer sp from runtime._panic and calling Runtime. recovery to trigger the goroutine dispatch, before which sp, PC, and the return values of the function are prepared.

The second part is mainly to do recover of panic, which is basically the same as the above process. He extracted the program counter PC and stack pointer sp from Runtime. _defer and called recovery function to trigger Goroutine. Call (SRC /runtime/asm_amd64.s 289);

// func mcall(fn func(*g))
// Switch to m->g0's stack, call fn(g).
// Fn must never return. It should gogo(&g->sched)
// to keep running g.The TEXT, the runtime McAll (SB), NOSPLIT, $0- 8 -
	MOVQ	fn+0(FP), DI

	get_tls(CX)
	MOVQ	g(CX), AX	// save state in g->sched
	MOVQ	0(SP), BX	// caller's PC
	MOVQ	BX, (g_sched+gobuf_pc)(AX)
	LEAQ	fn+0(FP), BX	// caller's SP
	MOVQ	BX, (g_sched+gobuf_sp)(AX)
	MOVQ	AX, (g_sched+gobuf_g)(AX)
	MOVQ	BP, (g_sched+gobuf_bp)(AX)

	// switch to m->g0 & its stack, call fn
	MOVQ	g(CX), BX
	MOVQ	g_m(BX), BX
	MOVQ	m_g0(BX), SI
	CMPQ	SI, AX	// if g == m->g0 call badmcall
	JNE	3MOVQ $runtime· badmCall (SB), AX JMP AX MOVQ SI, g(CX)// g = m->g0
	MOVQ	(g_sched+gobuf_sp)(SI), SP	// sp = m->g0->sched.sp
	PUSHQ	AX
	MOVQ	DI, DX
	MOVQ	0(DI), DI CALL DI POPQ AX MOVQ $runtime· badmCall2 (SB), AX JMP AX RETCopy the code

The go runtime environment has its own stack and goroutine, and the recovery function is executed in the Runtime environment. Therefore, we need to schedule m->g0 to execute the recovery function.

// Unwind the stack after a deferred function calls recover
// after a panic. Then arrange to continue running as though
// the caller of the deferred function returned normally.
func recovery(gp *g) {
	// Info about defer passed in G struct.
	sp := gp.sigcode0
	pc := gp.sigcode1

	// d's arguments need to be in the stack.
	ifsp ! =0 && (sp < gp.stack.lo || gp.stack.hi < sp) {
		print("recover: ", hex(sp), " not in [", hex(gp.stack.lo), ",", hex(gp.stack.hi), "]\n")
		throw("bad recovery")}// Make the deferproc for this d return again,
	// this time returning 1. The calling function will
	// jump to the standard return epilogue.
	gp.sched.sp = sp
	gp.sched.pc = pc
	gp.sched.lr = 0
	gp.sched.ret = 1
	gogo(&gp.sched)
}
Copy the code

In the recovery function, the two status codes in G are used to trace back the stack pointer SP and restore the program counter PC to the scheduler. Gogo is called to rescheduling G and restore G to the position where recover function was called. Goroutine continues execution. Recovery sets the return value of the function to 1 during scheduling. So what does this do? The answer is found in the DeferProc function:

//go:nosplit
func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn. omit// deferproc returns 0 normally.
	// a deferred func that stops a panic
	// makes the deferproc return 1.
	// the code the compiler generates always
	// checks the return value and jumps to the
	// end of the function if deferproc returns ! = 0.
	return0()
	// No code can go here - the C return register has
	// been set and must not be clobbered.
}
Copy the code

When the runtime. deferProc function returns 1, the code generated by the compiler jumps directly to runtime.deferreturn before the caller returns. After jumping to the Runtime.deferturn function, the program has already returned to its normal logic from Panic.

  • Part five, if not encounteredruntime.gorecoverI’m going to go through all of themruntime._defer, called at the endfatalpanicAbort the program and printpanicParameter returns error code 2.
// fatalpanic implements an unrecoverable panic. It is like fatalthrow, except
// that if msgs ! = nil, fatalpanic also prints panic messages and decrements
// runningPanicDefers once main is blocked from exiting.
//
//go:nosplit
func fatalpanic(msgs *_panic) {
	pc := getcallerpc()
	sp := getcallersp()
	gp := getg()
	var docrash bool
	// Switch to the system stack to avoid any stack growth, which
	// may make things worse if the runtime is in a bad state.
	systemstack(func(a) {
		ifstartpanic_m() && msgs ! =nil {
			// There were panic messages and startpanic_m
			// says it's okay to try to print them.

			// startpanic_m set panicking, which will
			// block main from exiting, so now OK to
			// decrement runningPanicDefers.
			atomic.Xadd(&runningPanicDefers, - 1)

			printpanics(msgs)
		}

		docrash = dopanic_m(gp, pc, sp)
	})

	if docrash {
		// By crashing outside the above systemstack call, debuggers
		// will not be confused when generating a backtrace.
		// Function crash is marked nosplit to avoid stack growth.
		crash()
	}

	systemstack(func(a) {
		exit(2()}) * *int) (nil) = 0 // not reached
}
Copy the code

In this case, Runtime. fatalPanic implements an unrecoverable program crash by printing all panic messages and the parameters passed in during the call through Runtime. printpanics before aborting the program.

Ok, so that’s the whole Gopanic method, so let’s take a look at the GoRecover method.

gorecover

This function is much simpler and requires less code. Let’s have a look at the code:

// The implementation of the predeclared function recover.
// Cannot split the stack because it needs to reliably
// find the stack segment of its caller.
//
// TODO(rsc): Once we commit to CopyStackAlways,
// this doesn't need to be nosplit.
//go:nosplit
func gorecover(argp uintptr) interface{} {
	// Must be in a function running as part of a deferred call during the panic.
	// Must be called from the topmost function of the call
	// (the function used in the defer statement).
	// p.argp is the argument pointer of that topmost deferred function call.
	// Compare against argp reported by caller.
	// If they match, the caller is the one who can recover.
	gp := getg()
	p := gp._panic
	ifp ! =nil&&! p.goexit && ! p.recovered && argp ==uintptr(p.argp) {
		p.recovered = true
		return p.arg
	}
	return nil
}
Copy the code

If the current Goroutine does not call panic, the function will directly return nil. The four criteria for determining whether the panic can be recovered must be consistent. P. goexit determines whether the current state is triggered by Goexit. If it is, it cannot revocer live. Recover will be performed in Gopanic as mentioned above. Argp is the argument pointer of the top-level delay function call. Compare argp with the caller’s argp. If the match indicates that the caller can recover, set the recovered field to true. The main function here is to determine whether panic can recover. The specific recovery logic is still taken care of by the Gopanic function.

The process to summarize

Read a source code above, must be a face meng force ~. This is normal, after all, the text, can only go to this extent, or to their own combined with go to see, here is only a supporting role, and finally do a process summary.

  • If encountered during the execution of the programpanic, then will callruntime.gopanic, and then take the currentGoroutinethedeferThe linked list executes in turn.
  • In the calldeferThe function is if there isrecoverIs calledruntime.gorecoverIn thegorecoverIn theruntime._panicIn therecovedMarked astrueThe recovery logic is still in placeruntime.panicIn the.
  • ingopanicWill performdeferInline optimization, program recovery logic. In program recovery logic, it is determined if the trigger isruntime.Goexit, will also be carried outrecovery.panicAlso will berecoveryThe main logic isruntime.gopanicfromruntime._deferFetch the program counter from the structurepcAnd the stack pointerspAnd call theruntime.recoveryFunction recovery program.runtime.recvoeryIt’s going to be passed inpcspgogoJump in the backruntime.deferprocIs called if the return value is 1runtime.deferreturnRestore the normal flow.
  • ingopanicExecute all of them_deferAnd it didn’trecover, then it will be executedruntime.fatalpanicTerminates the program with error code 2.

That’s the logic. I’m tired…

Small eggs

To avoid panic, pay attention to the following:

  • Array/slice subscript out of bounds, forgoFor such a static language, subscript overshooting is a fatal problem.
  • Do not access uninitialized Pointers ornilPointer to the
  • Don’t move onclosethechanSend data in
  • mapNot thread-safe, do not read and write concurrentlymap

These are the typical ones. There are many other places where panic can occur. Let you learn by yourself.

conclusion

Well, that’s all for this article, the three qualities (share, like, read) are the author’s motivation to continue to create more quality content!

We have created a Golang learning and communication group. Welcome to join the group and we will learn and communicate together. Join the group: add me vX pull you into the group, or the public number to get into the group two-dimensional code

At the end, I will send you a small welfare. Recently, I was reading the book [micro-service architecture design mode], which is very good. I also collected a PDF, which can be downloaded by myself if you need it. Access: Follow the public account: [Golang Dreamworks], background reply: [micro service], can be obtained.

I have translated a GIN Chinese document, which will be maintained regularly. If you need it, you can download it by replying to [GIN] in the background.

Translated a Machinery Chinese document, will be regularly maintained, there is a need for friends to respond to the background [Machinery] can be obtained.

I am Asong, an ordinary programming ape. Let’s get stronger together. We welcome your attention, and we’ll see you next time

Recommended previous articles:

  • Mechanics-go Asynchronous task queues
  • Detail the implementation mechanism of defer
  • You really understand interface
  • Leaf-segment Distributed ID Generation System (Golang implementation version)
  • 10 GIFs to help you understand sorting algorithms (with go implementation code)
  • Go parameter transfer type
  • Teach my sister how to write message queues
  • Cache avalanche, cache penetration, cache breakdown
  • Context package, read this article enough!!
  • Go -ElasticSearch: How to get started
  • Interviewer: Have you used for-range in go? Can you explain the reasons for these problems