pupuk / blog

My New Blog. Record & Share. Focus on PHP, MySQL, Javascript and Golang.
MIT License
9 stars 2 forks source link

从Fiber的ctx.Redirect()函数看,参数的默认值 #37

Open pupuk opened 2 years ago

pupuk commented 2 years ago

引言:

很多语言,函数是允许有默认值的。 比如下面一个php的例子:

function NewUser($name, $age=1, $country='CN'){
    echo $name, $age, $country;
}
NewUser("张三");

结果是:张三1CN

而在Golang中,这种为函数设置默认值的方式,语言层面并不支持。Golang的设计哲学认为,这种隐式的调用,其实也是一种函数的重载,不能明确的地方,容易给使用者造成误解。

Golang的函数不支持隐式的,默认参数

如下的写法,会报错,编译器不能通过。 image

即使是这样写,还是会报错。 image image

如何取巧

看一个Fiber ctx.go中,有重定向URL的代码。调用时,既可以只传入location一个参数,也可以传入location, status两个参数,都不会报错。 https://github.com/gofiber/fiber/blob/master/ctx.go#L943

// Redirect to the URL derived from the specified path, with specified status.
// If status is not specified, status defaults to 302 Found.
func (c *Ctx) Redirect(location string, status ...int) error {
    c.setCanonical(HeaderLocation, location)
    if len(status) > 0 {
        c.Status(status[0])
    } else {
        c.Status(StatusFound)
    }
    return nil
}

原来他是用不定参数(...)这种方式来实现的,不定参数跟多返回值,是golang里面非常不错的语法糖。🚀

status ...int 其本质是一个int的slice。 c.Redirect("https://www.baidu.com/") 其接收到的参数是 [] int c.Redirect("https://www.baidu.com/", 301) 其接收到的参数是 [301] c.Redirect("https://www.baidu.com/", 301, 302) 其接收到的参数是 [301, 302]

仔细分析Redirect函数发现,如果传入301,302这种多个状态码,它也只取了第一个 c.Status(status[0])。 那它为什么不直接再接受一个int参数呢?比如:func (c *Ctx) Redirect(location string, status int),这样的话,调用的时候就必须要传入2个参数了。 c.Redirect("https://www.baidu.com/") 这种写法是要报错的。 为了实现带默认的效果,用status ...int这种不定参数来取巧了。

Fiber里面有很多支持默认值的,这种函数的写法,虽然跟Golang的设计哲学有些偏差,开发者是否喜欢就见仁见智了。但我挺喜欢,毕竟调用起来是真爽。

证明 ...不定参数的本质是slice

func main() {
    echo("张三", "李四", "王五")
}

func echo(s ...string) {
    fmt.Println(reflect.TypeOf(s))
    for _, v := range s {
        fmt.Println(v, reflect.TypeOf(v))
    }
}

结果:

[]string
张三 string
李四 string
王五 string
func main() {
    echo()
}

func echo(s ...string) {
    fmt.Println(reflect.TypeOf(s), len(s))
    for _, v := range s {
        fmt.Println(v, reflect.TypeOf(v))
    }
}

结果: []string 0