Является ли плохим тоном выполнять поиск по карте и утверждение типа в одном выражении?

Я только что понял, что можно выполнить поиск карты и утверждение типа/интерфейса в одном выражении.

m := map[string]interface{}{
    "key": "the value",
}
if value, ok := m["key"].(string); ok {
    fmt.Printf("value exists and is a string: %s\n", value)
} else {
    fmt.Println("value does not exist or is not a string")
}

Это считается плохо? Я не видел никаких официальных документов, комментирующих это.

edit: я знаю, что этот код не может различить «ключ не существует» и «значение неправильного типа».

edit2: гм, печать в предложении else была неправильной :(


person pythonator    schedule 19.09.2017    source источник


Ответы (2)


То, что вы делаете, на самом деле работает, потому что вы используете специальную идиому с запятой в утверждении типа, которое не паникует, если утверждение не выполняется, и потому что карты могут быть проиндексированы с ключами, которых нет в ней (что приведет к нулевому значению типа значения карты).

Это правда, что с этим вы не можете сказать, есть ли ключ в карте или нет, или он есть, но его значение nil, но вы уже подозревали это, как будто утверждение не выполняется, вы печатаете "значение делает не существует или не является строкой".

Чтобы протестировать все «угловые» случаи, см. этот пример:

m := map[string]interface{}{
    "key":  "the value",
    "key2": 2,
    "key3": nil,
    // "key4":"", // Uncommented on purpose
}

for _, k := range []string{"key", "key2", "key3", "key4"} {
    if value, ok := m[k].(string); ok {
        fmt.Printf("[key: %s] value exists and is a string: %s\n", k, value)
    } else {
        fmt.Printf("[key: %s] value does not exist or is not a string: %s\n",
            k, value)
    }
}

Вывод (попробуйте на Go Playground):

[key: key] value exists and is a string: the value
[key: key2] value does not exist or is not a string: 
[key: key3] value does not exist or is not a string: 
[key: key4] value does not exist or is not a string: 

Таким образом, вы можете использовать это, от этого не произойдет ничего плохого (например, паника или утечка памяти), просто знайте его пределы (например, вы не можете получить значение, связанное с "key2", поскольку оно не относится к типу string ).

Если вы хотите получить значение ключа, если он существует и имеет тип string, то именно это и делает ваш код. Хотя вам следует избегать структур данных и конструкций там, где они вам нужны, так как их сложнее понять и поддерживать в больших проектах.

Под этим я подразумеваю, например, что если в какой-то момент вашего кода вы ожидаете, что ключ "somekey" будет иметь связанное значение string, а это не так, вы не сразу узнаете, почему это так; это потому, что карта не содержит этого ключа, или это потому, что он есть, но со значением неправильного типа (или значение может быть даже nil)? Необходимо дальнейшее тестирование/отладка, чтобы отследить первопричину.

person icza    schedule 19.09.2017
comment
Да, я провел небольшой тест на игровой площадке, чтобы проверить все комбинации, и он действительно работает (то есть не паникует). Мне было в основном любопытно, была ли это неподдерживаемая причуда или что-то действительно задуманное. - person pythonator; 19.09.2017
comment
Мне было интересно то же самое, это работает, но спецификация языка ничего об этом не говорит. Я забыл тот факт, что поиск карты возвращает nil, если с этим ключом было добавлено замечание... - person Tim; 03.06.2019
comment
@Tim Индексирование карты возвращает нулевое значение типа значения карты, которое равно nil для interface{}, но, например, это 0 для int. Например. map[string]int{}["a"] приведет к 0, а не nil. - person icza; 04.06.2019
comment
Да, я неявно имел в виду map[string]interface{}. Было бы невозможно сделать утверждение типа, если бы это было не так;). Спасибо. - person Tim; 04.06.2019

Это считается плохо?

Нет, это явно неправильно. Вы не можете совмещать поиск по карте и утверждение ввода таким образом.

Что делает ваш код:

  1. Он ищет «ключ» на карте unconditional. Если «ключ» отсутствует на карте, он даст ноль (поскольку это нулевое значение интерфейса {}).

  2. Значение, полученное из карты (возможно nil, если оно отсутствует в карте, является типом, утвержденным для строки.

Ваш код не может определить, находится ли «ключ» на карте или нет.

person Volker    schedule 19.09.2017
comment
Вы не можете комбинировать поиск по карте и утверждение типа, как это. - Очевидно, вы можете, если этот код компилируется. ... Ваш код не может определить, есть ли ключ на карте или нет. - ОП никогда не заявлял, что хочет это определить. - person Gavin; 19.09.2017
comment
Возможно, я должен был заявить об этом более ясно (я предположил, что это было очевидно по сообщениям, которые я печатал); Я не могу отличить отсутствующий ключ от неправильного типа значения, используя этот ярлык. - person pythonator; 19.09.2017
comment
Точно не можете. Что делает ваш код неясным. Это не правильно. Вы делаете простое утверждение типа comm-okay, и поиск по карте не имеет к этому никакого отношения. Это все равно, что удивляться, что (2*3)+4 — это умножение и сложение за один шаг. Нет, это не так. Это просто умножить и просто добавить. С одним отличием: это ясно. - person Volker; 19.09.2017
comment
@Volker, это явно не так. Это правильно и ведет себя так, как ожидалось от OP. Вместо этого это может быть неясно для читателей, но не неправильно и не плохо. Смотрите ответ Ицы. - person Theophile Dano; 19.09.2017
comment
Я легко признаю, что это несколько неясно. Я никогда не говорил, что это предпочтительный способ сделать это. Это нормально (по крайней мере, в моем конкретном случае), что код действует только в том случае, когда ключ существует и значение имеет правильный тип, и ничего больше. То есть его поведение одинаково для несуществующего и некорректного типа. Если желание состоит в том, чтобы отсеять все существующие случаи, но с плохими данными, то да, это не поможет. - person pythonator; 19.09.2017