Как использовать структуру, которая была объявлена в другой функции? GO
Начинаю учить языки со статической типизацией. После динамической просто вынос мозга. Пишу веб-сервер на GO. У меня есть 2 функции (пример ниже), могу ли я объявить структуру в одной, а использовать в другой? Мой пример (он не работает)
package main
import (
"fmt"
"encoding/json"
)
// эта функция неизменна и переиспользуется, поэтому я не могу объявлять структуру в ней
func getRequestData(Form type) {
/*
Здесь должен быть обработчик запроса. Он берёт json из запроса,
заносит данные в структуру и возвращает её, что-то типа:
*/
var form Form
jsonString := `{"username": "admin", "password": "1234"}`
json.Unmarshal([]byte(jsonString), &form)
return form
}
func main() {
type loginForm struct {
username string
password string
}
data := getRequestData(loginForm)
fmt.Println(data)
}
Ответы (1 шт):
Короткий ответ: да, можно, но только через reflection.
Длинный ответ.
В Go есть два механизма работы со значениям - статическая типизация и reflection.
В выражении json.Unmarshal([]byte(jsonString), &form) используется именно reflection. Метод Unmarshal в динамике добудет описание фактического типа переменной form, узнает имена, типы и смещения полей относительно начала значения, и заполнит их десереализованными значениями. Этому методу нет разницы, где именно объявлен тип переменной form, ибо с этим типом метод работает в динамике.
Но! До того, как дело дойдет до Unmarshal, компилятор должен обработать вызов функции getRequestData, и на этом ваш код безнадёжно сломается.
Имена типов в Go не являются идентификаторами объектов. Ключевое слово type нельзя использовать в описании параметра. Параметр функции не может быть типом, имя параметра нельзя использовать как имя типа при объявлении переменной.
Добиться того, что вам нужно, можно с типом interface{}. Этот тип в Go обозначает произвольный тип
package main
import (
"fmt"
"encoding/json"
)
func getRequestData() interface{} {
var form interface{}
jsonString := `{"username": "admin", "password": "1234"}`
json.Unmarshal([]byte(jsonString), &form)
return form
}
func main() {
type loginForm struct {
username string
password string
}
data := getRequestData()
fmt.Println(data)
}
Такая функция извлечёт данные из строки и сохранит их в map. Поля json структуры будут доступны как ключи в отображении: data["username"] и data["password"]
Однако, забавная особенность json.Unmarshal в том, что эта функция полностью соответствует динамической типизации. Она сама проверит состав полей в структуре-параметре и постарается её корректно восстановить из JSON.
package main
import (
"encoding/json"
"fmt"
)
func getRequestData(form interface{}) error {
jsonString := `{"username": "admin", "password": "1234"}`
// json.Unmarshal сам увидит, как устроена структура form,
// и постарается её корректно восстановить
return json.Unmarshal([]byte(jsonString), form)
}
type LoginData struct {
Username string
Password string
}
func main() {
var form LoginData
err := getRequestData(&form)
if err != nil {
panic(err)
}
fmt.Println(form)
}
Вам достаточно передать указатель на ожидаемую структуру и проверить возвращаемую ошибку.
UPD.
Отвечая на исходный вопрос. Так как json.Unmarshal работает со значениями через reflection, то эта функция работает с любыми типами, объявленными где угодно.
В моём ответе LoginData вынесена на уровень пакета (и это кошерно), но можно было бы объявить тип внутри функции (хоть это и неправославно). Сработает. Главное, поля поименовать с большой буквы, иначе их через reflection нельзя изменить, и go их проигнорирует.
package main
import (
"encoding/json"
"fmt"
)
func getRequestData(form interface{}) error {
jsonString := `{"username": "admin", "password": "1234"}`
// json.Unmarshal сам увидит, как устроена структура form,
// и постарается её корректно восстановить
return json.Unmarshal([]byte(jsonString), form)
}
func main() {
type LoginData struct {
Username string
Password string
}
var form LoginData
err := getRequestData(&form)
if err != nil {
panic(err)
}
fmt.Println(form)
}