Hello! New to golang and I’m trying out a simple REST API with Fiber and MGM (MongoDB).
I am to understand how variables are passed. Consider the function below…
func CreateTodo(ctx *fiber.Ctx) {
params := new(struct {
Title string
Desc string
})
ctx.BodyParser(¶ms)
if len(params.Title) == 0 {
ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"ok": false,
"error": "Title not specified",
})
return
}
todo := models.CreateTodo(
params.Title,
params.Desc,
)
if err := mgm.Coll(todo).Create(todo); err != nil {
ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"ok": false,
"error": err.Error(),
})
return
}
ctx.JSON(fiber.Map{
"ok": true,
"todo": todo,
})
}
Focusing on these lines…
todo := models.CreateTodo(
params.Title,
params.Desc,
)
if err := mgm.Coll(todo).Create(todo); err != nil {
why dont most people write if err := mgm.Coll(&models.Todo{}).Create(todo); err != nil {
since that is valid? My understanding is the mgm.Coll needs the “structure” of the collection to find
also, why isnt if err := mgm.Coll(models.Todo).Create(todo); err != nil { valid? isn’t the type enough for the function to determine the structure? why do we create a new instance with &models.Todo{}?
This is like the distinction between “manifest typing” vs. “type deduction” for variables: Manifest typing explicitly defines the type of each variable (e.g. var todo *Todo = CreateTodo("title", "description")) where type deduction lets you write just var todo = CreateTodo("title", "description") or, more commonly, todo := CreateTodo("title", "description"))
Some people like the explicit types because the type of the result of every expression is written in front of you without you needing to lookup what each function returns. Personally, I hate manifest typing and would choose mgm.Coll(todo) over mgm.Coll(&models.Todo{}). What if we wanted to rename Todo to ToDo? You’d have to replace every occurrence either manually or with tooling. What if Todos get expanded in the future so that there are DependentTodos and/or ExternalTodos, etc., so that CreateTodo is changed to return an interface? Now mgm.Coll(&models.Todo{}) gets you the wrong type, but type deduction would have picked the right collection if you said mgm.Coll(todo).
Another good question: Some programming languages allow both values and types to be used as parameters to functions, other types, etc. (e.g. In C-like languages, “value parameters” are often enclosed in parentheses ((, )) and “type parameters” are often enclosed in “angle brackets” (<, >)). Go only allows values to be used as parameters, but to get around that limitation, you do get reflection.
Inside of mgm.Coll, it probably checks the type of its parameter and returns some collection-specific information it already has for that type, or, if it’s the first time mgm.Coll was called for an instance of that type, it generates whatever collection-specific information it needs about its parameter type.