вторник, 22 января 2019 г.

Эффективный Go: оператор switch

switch в Go более общий, чем в C. Выражения не должны быть константами или даже целыми числами, cases оцениваются сверху вниз, пока не будет найдено совпадение, и если switch не имеет выражения, он включается на true (то есть как будто бы было указано switch(true){}). Поэтому возможно - и идиоматично - писать if-else-if-else цепочку как switch.

func unhex(c byte) byte {
    switch {
    case '0' <= c && c <= '9':
        return c - '0'
    case 'a' <= c && c <= 'f':
        return c - 'a' + 10
    case 'A' <= c && c <= 'F':
        return c - 'A' + 10
    }
    return 0
}

Нет автоматического провала (то есть случая использования нескольких case одновременно для объединения их в один результат), но случаи (cases) могут быть представлены в разделенных запятыми списках.

func shouldEscape(c byte) bool {
    switch c {
    case ' ', '?', '&', '=', '#', '+', '%':
        return true
    }
    return false
}

Хотя они не так распространены в Go, как в других C-подобных языках, операторы break могут использоваться для раннего завершения switch. Иногда, однако, необходимо выйти из окружающего цикла (не switch цикла), и в Go это можно сделать, поставив метку в цикле и выходя (breaking) к этой метке. Следующий пример показывает оба варианта использования.

Loop:
    for n := 0; n < len(src); n += size {
        switch {
        case src[n] < sizeOne:
            if validateOnly {
                break
            }
            size = 1
            update(src[n])

        case src[n] < sizeTwo:
            if n+1 >= len(src) {
                err = errShortInput
                break Loop
            }
            if validateOnly {
                break
            }
            size = 2
            update(src[n] + src[n+1]<<shift)
        }
    }

Конечно, оператор continue также принимает необязательную метку, но это относится только к циклам.

Вот процедура сравнения для байтовых срезов, которая использует два оператора switch:

// Compare возвращает число сравнивая два байтовых среза,
// лексикографически.
// Результат будет 0 если a == b, -1 если a < b, 
// и +1 если a > b
func Compare(a, b []byte) int {
    for i := 0; i < len(a) && i < len(b); i++ {
        switch {
        case a[i] > b[i]:
            return 1
        case a[i] < b[i]:
            return -1
        }
    }
    switch {
    case len(a) > len(b):
        return 1
    case len(a) < len(b):
        return -1
    }
    return 0
}

Переключатель типов (Type switch)

switch также может быть использован для обнаружения динамического типа переменной интерфейса. Такой type switch использует синтаксис утверждения типа (type assertion) с ключевым словом type в круглых скобках. Если switch объявляет переменную в выражении, переменная будет иметь соответствующий тип в каждом условии. Также идиоматично повторно использовать имя в таких случаях, фактически объявляя новую переменную с тем же именем, но с разным типом в каждом случае.

var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
default:
    // %T печатает type, который t имеет
    fmt.Printf("неожидаемый тип %T\n", t)
case bool:
    // t имеет type bool
    fmt.Printf("boolean %t\n", t)
case int:
    // t имеет type int
    fmt.Printf("integer %d\n", t)
case *bool:
    // t имеет type *bool
    fmt.Printf("pointer к boolean %t\n", *t) 
case *int:
    // t имеет type *int
    fmt.Printf("pointer к integer %d\n", *t) 
}


Читайте также:


Комментариев нет:

Отправить комментарий