Объединение интерфейса и ввода в golang

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

import (
    "fmt"
)

type String interface {
    ~string | fmt.Stringer
}

Однако это дает ошибку cannot use fmt.Stringer in union (fmt.Stringer contains methods). Есть ли способ сделать это, не полагаясь на отражение или тип бокса/распаковки?

интерфейс типа String { fmt.Stringer /n ~string }

Para 17.05.2022 03:37

@Para Я не думаю, что это сработает, потому что для этого потребуется, чтобы все, что реализует String, наследовалось от string и реализовывало fmt.Stringer.

Woody1193 17.05.2022 04:04
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
3
2
36
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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


Самое близкое, что вы можете получить без использования отражения, - это использовать утверждение типа времени выполнения:

func StringLike(v any) string {

    if s, ok := v.(string); ok {
        return s
    }

    if s, ok := v.(fmt.Stringer); ok {
        return s.String()
    }

    panic("non string invalid type")
}

https://go.dev/play/p/p4QHuT6R8yO

Это решение, к которому я тоже пришел. Я надеялся, что дженерики Golang предложат эту функциональность; возможно, они будут в более поздней версии.

Woody1193 17.05.2022 04:33

Путаница может быть оправдана, потому что предложение параметров типа предлагает код, подобный вашему, однако, оказалось ограничением реализации в Go 1.18.

Он упоминается в характеристики и в примечаниях к выпуску Go 1.18. Спецификации являются нормативной ссылкой:

Implementation restriction: A union (with more than one term) cannot contain the predeclared identifier comparable or interfaces that specify methods, or embed comparable or interfaces that specify methods.

Существует также несколько обширный объяснение, почему это не было включено в выпуск Go 1.18. tl;dr упрощает вычисление наборов типов объединения (хотя в Go 1.18 наборы методов параметров типов также не вычисляются неявно...).

Учтите также, что с этим ограничением или без него вы, скорее всего, не получите ничего полезного, кроме передачи T функциям, использующим отражение. Чтобы вызвать методы на ~string | fmt.Stringer, вам все равно нужно ввести подтверждение или переключение типа.

Обратите внимание, что если целью такого ограничения является просто печать строкового значения, вы можете просто использовать fmt.Sprint, который использует отражение.

В более широком случае утверждение типа или переключатель, как в ответе colm.anseo, отлично работает, когда аргумент может принимать точные типы, такие как string (без ~) и fmt.Stringer. Для приближений вроде ~stringвы не можете исчерпывающе обрабатывать все возможные термины, потому что эти наборы типов практически бесконечны. Итак, вы вернулись к размышлению. Лучшей реализацией может быть:

func StringLike(v any) string {
    // switch exact types first
    switch s := v.(type) {
    case fmt.Stringer:
        return s.String()

    case string:
        return s
    }

    // handle the remaining type set of ~string
    if r := reflect.ValueOf(v); r.Kind() == reflect.String {
        return r.Convert(reflect.TypeOf("")).Interface().(string)
    }

    panic("invalid type")
}

Детская площадка: https://go.dev/play/p/rRD7QSNMkcz

Это отличное объяснение проблемы. С моей точки зрения, цель была где-то на полпути между предложенным вами исправлением и размышлением. Я хотел сгенерировать строку из любого типа, который реализует fmt.Stringer или саму строку, сохраняя при этом строгую проверку типов во время компиляции, но похоже, что это невозможно с go1.18. В любом случае, спасибо за подробный ответ.

Woody1193 17.05.2022 10:31

@ Woody1193, возможно, они снимут это ограничение в будущем. Комментарий, на который я дал ссылку из системы отслеживания проблем Go, намекает на то, что реализация союзов с методами выполнима. Разрешение типов тильды в переключателях также поможет, и это также может быть разрешено в будущем. В Go 1.18 все консервативно, чтобы избежать слишком больших проблем в случае критических недостатков дизайна. Альтернативой безопасности типов может быть объявление stringLike неэкспортируемым и иметь экспортированные методы доступа String[S ~string](v S) и Stringer(s fmt.Stringer), которые вызывают stringLike

blackgreen 17.05.2022 12:04

Верно, и это хороший момент

Woody1193 18.05.2022 01:24

Другие вопросы по теме