Errors in Go

Source:
(1)Why Go gets exception right
(3)Constant errors
(4)Stack traces and the errors package
(5)Don’t just check errors, handle them gracefully
(2)Error handling vs. exceptions redux
(3)Errors and Exceptions, redux
(4)Inspecting errors
(5)Error handling and Go
(6)Working with Errors in Go 1.13
(7)Defer, Panic, and Recover

Digest:

1. Errors:
Library routines must often return some sort of error indication to the caller. As memtioned earlier, Go's multivalue return makes it easy to return a detailed error description alongside the normal return value. It is good style to use this feature to provide detailed error information.
A library writer is free to implement this interfae with a richer model under the covers, making it possible not only to see the error but also to provide some context.

When feasible, error strings should identify their origin, such as by having a prefix naming the operation or package that generated the error.
Callers that care about the precise error details can use a type switch or a type assertion to look for specific errors and extract details.

2. Panic:
The usual way to report an error to a caller is to return an error as an extra return value. The canonical Read method is a well-known instance; it returns a byte count and an error. But what if the error is unrecoverable? Sometimes the program simply cannot continue.

For this purpose, there is built-in function panic that in effect creates a run-time error that will stop the program (but see the next section). The function takes a single argument of arbitrary type -- often a string -- to be printed as the program dies. It's also a way to indicate that something impossible has happened, such as exiting an infinite loop.

Real library functions should avoid panic. If the problem can be masked or worked around, it's always better to let things continue to run rather than taking down the whole program. One possible counterexample is during initialization: if the library truly cannot set itself up,it might be reasonable to panic, so to speak.

3. Recover:
When panic is called, including implicitly for run-time errors such as indexing a slice out of bounds or failing a type assertion, it immediately stops execution of the current function and begins unwinding the stack of the goroutine, running any deferred functions along the way. If that unwinding reaches the top of the goroutine's stack, the program dies. However, it is possible to use the built-in function recover to regain control of the goroutine and resume normal execution.

A call to recover stops the unwinding and returns the argument passed to panic. Because the only code that runs while unwinding is inside deferred functions, recover is only useful inside deferred functions.

One application of recover is to shut down a failing goroutine inside a server without killing the other executing goroutines.

func server(workChan <-chan *work) {
	for work := range workChan {
		go safelyDo(work)
	}
}

func safelyDo(work *Work) {
	defer func() {
		if err := recover(); err != nil {
			log.Println("work failed:", err)
		}
	}()
	do(work)
}

In this example, if do(work) panics, the result will be logged and the goroutine will exit cleanly without disturbing the others. There's no need to do anything else in the deferrd closure; calling recover handles the condition completely.

Because recover always returns nil unless called directly from a deferred function, deferred code can call library routines that themselves use panic and recover without failing. As an example, the deferred function in safelyDo might call a logging function before calling recover, and that logging code would run unaffected by the panicking state.

With our recovery pattern in place, the do function (and anything it calls) can get out of any bad situation cleanly by calling panic. We can use that idea to simplify error handling in complex software. Let's look at an idealized version of a regexp package, which reports parsing errors by calling panic with a local error type. Here's the definition of Error, an error method, and the Compile function:

// Error is the type of a parse error; it satisfies the error interface.
type Error string
func (e Error) Error() string {
	return string(e)
}

// error is a method of *Regexp that reports parsing errors by panicking with an Error.
func (regexp *Regexp) error(err string) {
	panic(Error(err))
}

// Compile returns a parsed representation of the regular expression.
func Compile(str string) (regexp *Regexp, err error) {
	regexp = new(Regexp)
	// doParse will panic if there is a parse error.
	defer func() {
		if e := recover(); e != nil {
			regexp = nil	// Clear return value.
			err = e.(Error)	// Will re-panic if not a parse error.
		}
	}()
	return regexp.doParse(str), nil
}

if doParse panics, the recovery block will set the return value to nil -- deferred functions can modify named return values. It will then check, in the assignment to err, that the problem was a parse error by asserting that it has the local type Error. If it does not, the type assertion will fail, causing a run-time error that continues the stack unwinding as though nothing had interrupted it. This check means that if something unexpected happens, such as index out of bounds, the code will fail even though we are using panic and recover to handle parse errors.

With error handling in place, the error method (because it's a method bound to a type, it's fine, even natural, for it to have the same name as the builtin error type) makes it easy to report parse errors without worrying about unwinding the parse stack by hand:

if pos == 0 {
	re.error("'*' illegal at start of expression")
}

Useful though this pattern is, it should be used only within package. Parse turns its internal panic calls into error values; it does not expose panics to its client. That is a good rule to follow.

By the way, this re-panic idiom changes the panic value if an actual error occurs. However, both the original and new failures will be presented in the crash report, so the root cause of the problem will still be visible. Thus this simple re-panic approach is usually sufficient -- it's a crash after all -- but if you want to display only the original value, you can write a little more code to filter unexpected problems and re-panic with the original error.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
As an AI language model, I do not have access to the logs to diagnose the specific errors that caused the manifest merger to fail. However, here are some general steps you can take to troubleshoot this issue: 1. Check the error message: The error message should provide some clues as to what went wrong during the manifest merger process. Look for specific error codes or messages that can help you narrow down the issue. 2. Check the manifest files: Make sure that all the manifest files are correctly formatted and do not have any syntax errors. Also, verify that there are no conflicting declarations, such as duplicate activity names or conflicting permissions. 3. Check the build.gradle files: Make sure that the dependencies and configurations in the build.gradle files are correctly specified and do not have any conflicts. Also, check that the versions of the dependencies are compatible with each other. 4. Update the Android Gradle Plugin: Make sure that you are using the latest version of the Android Gradle Plugin. You can update it by modifying the build.gradle file, as described in the official documentation. 5. Clean and rebuild the project: Sometimes, cleaning and rebuilding the project can resolve issues related to the manifest merger. To do this, go to the "Build" menu in Android Studio and select "Clean Project" and then "Rebuild Project". If none of these steps resolve the issue, you may need to seek help from the Android development community or contact Google support for further assistance.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值