Naguib Ihab
Naguib Ihab

Reputation: 4496

Return nil for a struct in Go

I am connecting to a database, getting a row and then sending it back to the user. What I want to do though is have a return statement if I can't find that row or if I have an error.

Because I'm returning a struct I can't return nil and I get that error cannot use nil as type Item in return argument (Item is my struct)

I read online that if I use a pointer in the return statement and return *Item instead of Item then I'd be able to pass nil, when I try to create item := *Item{} I get the following error invalid indirect of Item literal (type Item)

I think there are a few solutions to this but I can't find any, what I really want to know is:

Here's my code:

package main

import (
    "fmt"
    "github.com/aws/aws-lambda-go/lambda"

  "github.com/aws/aws-sdk-go/aws"
  "github.com/aws/aws-sdk-go/aws/session"
  "github.com/aws/aws-sdk-go/service/dynamodb"
  "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
)

type Request struct {
    Name string `json:"name"`
}

type Item struct {
  Name string `json:"name"`
  Stock int `json:"stock"`
  Price float64 `json:"price"`
}

func Handler(request Request) (Item, error) {
  sess, err := session.NewSession(&aws.Config{
    Region: aws.String("us-west-2")},
  )

  // Create DynamoDB client
  svc := dynamodb.New(sess)

  result, err := svc.GetItem(&dynamodb.GetItemInput{
    TableName: aws.String("Inventory"),
    Key: map[string]*dynamodb.AttributeValue{
      "name": {
          S: aws.String(request.Name),
      },
    },
  })

  if err != nil {
    fmt.Println(err.Error())
//     return nil, err
  }

  item := Item{}

  err = dynamodbattribute.UnmarshalMap(result.Item, &item)

  if err != nil {
    panic(fmt.Sprintf("Failed to unmarshal Record, %v", err))
//     return nil, err
  }

  if item.Name == "" {
      fmt.Println("Could not find item")
//       return nil, nil
  }

  fmt.Println("Found item:")
  fmt.Println("Name:  ", item.Name)
  fmt.Println("Stock: ", item.Stock)
  fmt.Println("Price:  ", item.Price)

    return item, nil
}

func main() {
    lambda.Start(Handler)
}

Upvotes: 68

Views: 119752

Answers (2)

K.Sy
K.Sy

Reputation: 110

But do you need a nil?

I don't know enough about AWS Lambdas to say whether Start requires a pointer or nil.

But if called APIs don't require it, and you know that you won't need to mutate the return value after the function call, then it might be better to return the zero value of the struct instead of a nil pointer. Admittedly, it's not as pretty looking as a nil in the return statement or comparisons, but it's safer.

But this is opinion.

Side note: mind the function name ending with -er, because if one ever turns that into a method with an interface, the latter would need to be called Handlerer if one wanted to stay within Go convention.

Upvotes: 0

Elias Van Ootegem
Elias Van Ootegem

Reputation: 76405

You're assigning your item variable wrong. You said you tried item := *Item{}, whereas the way to create a pointer is either through the use of the new builtin, or to create a literal, and the address-of operator (&). The latter is the approach you'll most commonly see in golang. There are some cases where one would use new, but in this case, I'd go for the second approach:

So either:

item := &Item{}
// or
item := new(Item)

Lastly, you can keep the code as-is, and just return a pointer at the end:

item := Item{}
// some code here
return &item, nil

In case where you have to return an error, you can still have return nil, err

So putting everything together:

// return *Item instead of Item
func Handler(request Request) (*Item, error) {
   // your code here, eg:
   item := Item{}
   if err := dynamodbattribute.UnmarshalMap(result.Item, &item); err != nil {
        return nil, err
    }
    return &item, nil
}

Alternatively, assign item as a pointer from the start

func Handler(request Request) (*Item, error) {
   // your code here, eg:
   item := &Item{}
   if err := dynamodbattribute.UnmarshalMap(result.Item, item); err != nil {
        return nil, err
    }
    return item, nil
}

Upvotes: 92

Related Questions