Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implications of defining a struct inside a function vs outside?

Tags:

go

Are there any implications (GC churn, performance, or otherwise) to defining an struct inside a function vs. having it defined outside? For example:

type Outside struct {
  Foo string `json:"foo"`
}

func SomeFunc(b []byte) error {
  outside := Outside{}

  if err := json.NewDecoder(b).Decode(&outside); err != nil {
    return err
  }

  ...
}

vs.

func SomeFunc(b []byte) error {

  type inside struct {
    Foo string `json:"foo"`
  }

  if err := json.NewDecoder(b).Decode(&inside); err != nil {
    return err
  }

  ...
}

Would there be any situations where one is preferred over the other?

like image 626
user7467314 Avatar asked Feb 02 '17 18:02

user7467314


People also ask

Can I define a struct inside a function?

Yes, the standard allows this, and yes, the name you create this way is only visible inside the function (i.e., it has local scope, just like when you define int i; , i has local scope). or, if you're really only going to use it once, struct { /* ...

Can struct be defined inside class?

Yes you can. In c++, class and struct are kind of similar. We can define not only structure inside a class, but also a class inside one. It is called inner class.

Can we declare structure inside main?

Note that, if we define the structure inside the main function, then it can not be passed to the functions; i.e. Lines 7-12 of Listing 6.6 can not be defined inside the main function, as it will generate error.

Can we write function inside structure in C?

1 Answer. No, you cannot define a function inside the structure of C Programming, but you can do so in C++, rather you can have a function pointer in a “struct” in C Language.


6 Answers

To me the main drawback for a type defined in a function is that you cannot define methods on that type.

See this example https://play.golang.org/p/cgH01cRwDv6:

package main  import (     "fmt" )  func main() {     type MyType struct {         Name string     }      // You cannot define a method on your type     // defined in a function, can you?      func (m MyType) String() string {       return m.Name     }      m := MyType{Name: "Hello, World!"}     fmt.Println(m) } 

The above example will fail with the error prog.go:15:27: expected ';', found 'IDENT' string (and 1 more errors).

like image 159
Konrad Kleine Avatar answered Oct 20 '22 22:10

Konrad Kleine


There is no performance difference – it's only a difference of scope (i.e., where the type definition can be seen). If you only need the type within a single function, it's fine to define it there.

As others have noted, if you define a type at the package level (i.e., outside of a function) with a name beginning with a capital letter, it will be exported (i.e., visible outside the package). If the name doesn't begin with a capital letter, it will only be visible within the package.

like image 23
Andy Schweig Avatar answered Oct 20 '22 20:10

Andy Schweig


My understanding is the difference is just in accessibility.

  • A struct defined starting with an upper case letter will be exportable, meaning it can be accessed from other packages.
  • A struct defined starting with a lower case letter can be accessed from anything within the same package but not externally.
  • A struct defined in a function inline can only be accessed/initialized by that function.
like image 22
Josh Wilson Avatar answered Oct 20 '22 20:10

Josh Wilson


For me I once defined a struct inside a function for marshalling JSON byte array ([]byte) into the struct instance, and extract a message from the instance.

Obviously it is not required to define the struct. I could have extracted the message by marshalling the JSON byte array into interface{} and then cast recursively to get the required message.

By defining the struct, extraction of the message becomes very easy :)

    var errDetail struct {
        Message string `json:"message"`
        Success bool   `json:"success"`
    }
    
    json.Unmarshal(*bytes, &errDetail)
    
    if errDetail.Message == "" {
        fmt.Println("error message is not present")
        return nil
    }
    return errDetail.Message
like image 40
sabbir Avatar answered Oct 20 '22 22:10

sabbir


As others have mentioned its all about limit the variables scope. If you are going to use a struct inside a function, you could also use an anonymous struct.

package main

import (
    "fmt"
)

func main() {

    m := struct {
        greeting string
        name     string
    }{
        greeting: "hello",
        name:     "world",
    }

    fmt.Printf("%v %v\n", m.greeting, m.name)
}

If you're only planing to use the struct inside the function you can define the fields of the struct and assign values to them right away.

like image 26
Grizzle Avatar answered Oct 20 '22 20:10

Grizzle


The scope is different, you can check in Golang spec here:

Most related parts are:

Go is lexically scoped using blocks:

The scope of an identifier denoting a constant, type, variable, or function (but not method) declared at top level (outside any function) is the package block.

The scope of a type identifier declared inside a function begins at the identifier in the TypeSpec and ends at the end of the innermost containing block.

And if you use go tool compile -S -N hello.go to check the generated assembly code, you can spot the names of type defined in package level and names of type defined inside function are different.

Package level

package main

import (
    "fmt"
)

type Point struct {
    X, Y int
}

func main() {
    fmt.Printf("%v\n", Point{X: 1, Y: 2})
}

Then try to compile and find this line: type."".Point SRODATA size=144.

Inside function

package main

import (
    "fmt"
)


func main() {
    type Point struct {
        X, Y int
    }
    fmt.Printf("%v\n", Point{X: 1, Y: 2})
}

Then try to find this line: type.*"".Point·1 SRODATA size=56

Than means, they just got different names after compiled.

like image 43
diabloneo Avatar answered Oct 20 '22 20:10

diabloneo