Как гарантировать наличие структурного поля, как это делают интерфейсы для методов? - PullRequest
0 голосов
/ 08 мая 2019

У меня есть несколько структур транзакций:

SpendTx
NameTransferTx
NameUpdateTx
...

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

func (tx *NameTransferTx) sizeEstimate() (int, error) {
    feeRlp, err := rlp.EncodeToBytes(tx.Fee)
    if err != nil {
        return 0, err
    }
    feeRlpLen := len(feeRlp)

    rlpRawMsg, err := tx.RLP()
    if err != nil {
        return 0, err
    }

    return len(rlpRawMsg) - feeRlpLen + 8, nil
}

Это много дублированного кода, все потому, что я не могу написать что-то вроде этого:

type Tx interface {
    RLP() ([]byte, error)
    Fee utils.BigInt // Golang won't allow this
}
func estimateSizeOfTx(tx Tx) (int, error) {
    feeRlp, err := rlp.EncodeToBytes(tx.Fee)
    if err != nil {
        return 0, err
    }
    feeRlpLen := len(feeRlp)

    rlpRawMsg, err := tx.RLP()
    if err != nil {
        return 0, err
    }

    return len(rlpRawMsg) - feeRlpLen + 8, nil
}

Конечно, я мог бы написать функцию получения, подобную getFee(), и сделать интерфейс из этого, но это не лучше. Или это так делают другие люди?

Ответы [ 2 ]

0 голосов
/ 09 мая 2019

Для оценки размера Tx требуется Tx.RLP () (гарантируется интерфейсом Tx) и поле Tx.Fee (которое существует постоянно, но не может быть подтверждено компилятором).

Оценка размера одинакова для всех типов транзакций. Итак, я нашел удовлетворительное решение, сделав оценку размера отдельной функцией, и я просто предоставляю комиссию за транзакцию в качестве аргумента функции:

// sizeEstimate returns the size of the transaction when RLP serialized, assuming the Fee has a length of 8 bytes.
func calcSizeEstimate(tx Tx, fee *utils.BigInt) (int, error) {
    feeRlp, err := rlp.EncodeToBytes(fee)
    if err != nil {
        return 0, err
    }
    feeRlpLen := len(feeRlp)

    rlpRawMsg, err := tx.RLP()
    if err != nil {
        return 0, err
    }

    return len(rlpRawMsg) - feeRlpLen + 8, nil
}

...
func (tx *SpendTx) sizeEstimate() (int, error) {
    return calcSizeEstimate(tx, &tx.Fee)
}
...
func (tx *NamePreclaimTx) sizeEstimate() (int, error) {
    return calcSizeEstimate(tx, &tx.Fee)
}
...
func (tx *NameClaimTx) sizeEstimate() (int, error) {
    return calcSizeEstimate(tx, &tx.Fee)
}
...
0 голосов
/ 08 мая 2019

Ты не.Если вас не интересует конкретный тип (т. Е. Вы можете использовать интерфейс), то вам не важны данные, а только их поведение (поэтому интерфейсы указывают только методы).Если вы заботитесь о данных, то вы уже по колено во внутренностях типа, и вам следует использовать конкретный конкретный тип (или использовать переключатель утверждения типа / типа, чтобы добраться до него).Это не специфично для Go;большинство языков не позволяют указывать поля в интерфейсах.

Получатель не был бы худшей вещью.Вы также можете инкапсулировать поля, к которым вам нужен прямой доступ, в их собственный тип структуры, и иметь для этого геттер (если есть несколько связанных полей, с которыми вы хотите работать вместе).Наконец, вы можете переделать общий дизайн, чтобы устранить или перенести необходимость.Здесь недостаточно контекста, чтобы сделать жесткое предложение, но, чтобы сделать общее предположение, кажется, что у вас, вероятно, есть что-то, что записывает эти транзакции куда-то, и вас интересует размер того, что должно быть написано.Поскольку также кажется, что вы выполняете всю связанную обработку для создания сообщения в методе, где вы пытаетесь получить его окончательную длину, вы не могли бы просто создать сообщение как []byte, взять его длину изатем использовать тот же вывод, когда вы делаете ... что бы вы ни делали с ними?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...