Каков идиоматический способ вернуть структуру или ошибку?

У меня есть функция, которая возвращает либо Card, что является типом struct, либо ошибкой.

Проблема в том, как я могу вернуться из функции при возникновении ошибки? nil недопустим для structs, и у меня нет допустимого нулевого значения для моего типа Card.

func canFail() (card Card, err error) {
    // return nil, errors.New("Not yet implemented"); // Fails
    return Card{Ace, Spades}, errors.New("not yet implemented"); // Works, but very ugly
}

Единственным обходным решением, которое я нашел, является использование *Card, а не Card, a сделать его либо nil, когда есть ошибка, или заставить его указывать фактическое Card, когда ошибка не возникает, но это довольно неуклюжий.

func canFail() (card *Card, err error) {
    return nil, errors.New("not yet implemented");
}

Есть ли лучший способ?

EDIT: Я нашел другой способ, но не знаю, является ли это идиоматическим или даже хорошим стилем.

func canFail() (card Card, err error) {
    return card, errors.New("not yet implemented")
}

Так как Card является именованным возвращаемым значением, я могу использовать его без его инициализации. Он обнуляется по-своему, мне все равно, поскольку вызывающая функция не должна использовать это значение.

Ответ 1

func canFail() (card Card, err error) {
    return card, errors.New("not yet implemented")
}

Я думаю, что это, ваш третий экзамен, тоже прекрасен. Понятное правило заключается в том, что когда функция возвращает ошибку, другие возвращаемые значения нельзя полагаться на значимые значения, если документация явно не объясняет иначе. Таким образом, возврат значения бессмысленной структуры здесь отлично.

Ответ 2

Например,

type Card struct {
}

func canFail() (card Card, err error) {
    return Card{}, errors.New("not yet implemented")
}

Ответ 3

func canFail() (card Card, err error) {
        if somethingWrong {
                err = errors.New("Not yet implemented")
                return
        }

        if foo {
                card = baz
                return
        }

        ... 

        // or 
        return Card{Ace, Spades}, nil
}

Ответ 4

Для меня я предпочитаю твой второй вариант.

func canFail() (card *Card, err error) {
    return nil, errors.New("not yet implemented");
}

Таким образом, вы можете быть уверены, что при возникновении ошибок вызывающие canFail() не смогут использовать card так как она равна нулю. Мы не можем быть уверены, что вызывающие абоненты сначала проверят ошибку.

Ответ 5

В качестве альтернативы возврату структуры вы можете подумать о том, чтобы позволить вызывающему абоненту выделить ее и параметры набора функций.

func canFail(card *Card) (err error) {
    if someCondition {
        // set one property
        card.Suit = Diamond

        // set all at once
        *card = Card{Ace, Spade}
    } else {
        err = errors.New("something went wrong")
    }

    return
}

Если вам неудобно притворяться, что Go поддерживает ссылки на стиль С++, вы также должны проверить card на nil.

https://play.golang.org/p/o-2TYwWCTL

Ответ 6

Я предлагаю вам использовать указатели вместо типа, так как он создает пустой объект типа, если вы получаете какую-либо ошибку. вместо этого верните указатель типа и верните nil, когда вы получите ошибку.

func canFail() (card *Card, err error) {
    .....
    if err != nil { // If error occurs
        return nil, errors.New("not yet implemented")
    }
    return Card{Ace, Spades}, nil;
}