面试题答案
一键面试1. 避免错误处理代码重复
- 封装错误处理函数:
在实际项目中,许多函数可能会返回类似的错误类型,如文件操作的
os.Error
等。可以封装一个通用的错误处理函数,将错误日志记录、错误返回等操作统一处理。例如:
package main
import (
"fmt"
"os"
)
func handleError(err error, msg string) {
if err != nil {
fmt.Printf("%s: %v\n", msg, err)
os.Exit(1)
}
}
func readFileContent(filePath string) string {
data, err := os.ReadFile(filePath)
handleError(err, fmt.Sprintf("Failed to read file %s", filePath))
return string(data)
}
这样在多个函数中调用handleError
,减少了重复的if err != nil
代码块。
- 使用错误接口类型断言:
对于一些有共性的错误类型,可以使用类型断言来统一处理。例如,在网络编程中,
net.Error
接口有Timeout
等方法。可以这样处理:
package main
import (
"fmt"
"net"
)
func connectToServer() {
conn, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil {
if ne, ok := err.(net.Error); ok && ne.Timeout() {
fmt.Println("Connection timeout")
} else {
fmt.Printf("Other error: %v\n", err)
}
}
defer conn.Close()
}
通过类型断言将不同的错误情况进行分类处理,避免重复的全面错误判断。
2. 合理传递和包装错误信息
- 使用
fmt.Errorf
包装错误: 当一个函数调用另一个函数并需要传递错误时,使用fmt.Errorf
包装错误可以添加更多上下文信息。例如:
package main
import (
"fmt"
"os"
)
func readConfigFile(filePath string) ([]byte, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("failed to read config file %s: %w", filePath, err)
}
return data, nil
}
这里fmt.Errorf
的%w
动词保留了原始错误,使得上层调用者可以通过errors.Unwrap
获取原始错误,同时添加了具体的上下文信息“failed to read config file %s”。
- 自定义错误类型和接口: 对于项目中特定领域的错误,可以定义自定义错误类型和接口。例如,在一个电商系统中:
package main
import (
"errors"
"fmt"
)
// 自定义错误类型
type InsufficientStockError struct {
ProductID string
Stock int
Requested int
}
func (ise InsufficientStockError) Error() string {
return fmt.Sprintf("product %s has only %d in stock, requested %d", ise.ProductID, ise.Stock, ise.Requested)
}
func checkStock(productID string, requested int) error {
// 假设实际从数据库获取库存
stock := 5
if requested > stock {
return InsufficientStockError{
ProductID: productID,
Stock: stock,
Requested: requested,
}
}
return nil
}
这样在调用checkStock
的地方,可以根据自定义错误类型进行针对性处理,使得错误处理更加清晰和易于维护。同时,自定义错误类型可以携带更多与业务相关的信息。