64

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:

  • How to return a pointer *Item instead of Item
  • Is there another way to return nil for a struct?

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)
}
1
  • 1
    item := *Item{} should be item := &Item{} Commented Jun 5, 2018 at 10:34

1 Answer 1

89

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
}
5
  • 2
    This may be obvious but just to be sure OP ought to also update the return type from Item to *Item in the Handlers declaration.
    – mkopriva
    Commented Jun 5, 2018 at 11:47
  • 1
    @mkopriva: Updated, included the UnmarshalMap bit so item is being passed correctly based on how the var is initialised Commented Jun 5, 2018 at 12:30
  • 1
    Can someone explain why one cannot merely return nil, err without first declaring item := &Item{} or item := Item{}? Commented Jan 28, 2019 at 19:40
  • 2
    @RylanSchaeffer because item is passed as an argument to UnmarshalMap. If you don't declare it, you can't use it (ie pass it as an argument). You need the variable before you use it. If you don't want to allocate any memory unless you need to, just replace item := Item{}, with var item Item, pass it as &item to the UnmarshalMap call, and return &item on success, nil on error. var item Item won't allocate the object unless until it's needed, it's mentioned in the language spec somewhere. Commented Jan 28, 2019 at 19:47
  • 1
    @RylanSchaeffer nice one. var item T that is my answer :-)
    – ADM-IT
    Commented Nov 25, 2022 at 15:33

Not the answer you're looking for? Browse other questions tagged or ask your own question.