Adding method to a golang struct in a different file

12,075

Solution 1

They are in the same package.

They are not in the same package. A Go package has both a name and a path. They're both named routes but they have different paths. The actual packages are routes and controller/routes. The result is subdirectories are different packages.

See Package Names on the Go Blog for more information.

Since they're in different packages, they can only access each other's public members and exported methods. You can't monkey patch someone else's package or interface in Go. This is by design to keep all the functionality of a package in one place, no action-at-a-distance.

You have options. You could put all methods of routes into a single package. If they all belong together, there's no need to split it up into multiple files.

If they don't really belong together, you can write a new struct with routes embedded into it and define new methods on that. Then you can access either the wrapper struct to get your added methods, or its embedded struct to get routes' methods. See this answer for an example.


But really I think you need to think about how your code is arranged. The App probably shouldn't be defined by the routes package, they should be separate. Instead, Go prefers a has-a relationship. App would contain an instance of routes.Route.

You'd rearrange your code tree like so:

app/
    app.go
    app_test.go
    routes/
        routes.go
        routes_test.go

Note that rather than having it all in src/ it's now contained in its own project directory. app.go would look something like this.

// src/app/app.go
package app

import(
    "app/routes"
    "fmt"
    "net/http"
)

type App struct {
    routes routes.Route
}

func (a *App) initializeRoutes() {
    a.routes.AddRoute("/products", a.getSomething)
}

func (a *App) getSomething(w http.ResponseWriter, r *http.Request) {
    fmt.Println("Something!")
}

Notice how we're delegating responsibility to adding a route to a.routes rather than having App do it itself. This avoids the desire to smash all functionality into one ginormous package. routes.Route would be defined in app/routes/routes.go.

// src/app/routes/routes.go
package routes

import "net/http"

// A type specifying the interface for route handlers.
type RouteHandler func(w http.ResponseWriter, r *http.Request)

type Route struct {
    handlers map[string]RouteHandler
}

func (r *Route) AddRoute(path string, handler RouteHandler) {
    r.handlers[path] = handler
}

Now all routes has to worry about is handling routes. It doesn't know anything about your specific application logic.


I was trying to get my http.res & http.req functions in a controllers file.

Now that we've rearranged the file structure, you can do that. You can, if you like, define app/controllers.go to organize your code.

// src/app/controllers.go
package app

import(
    "fmt"
    "net/http"
)

func (a *App) getSomething(w http.ResponseWriter, r *http.Request) {
    fmt.Println("Something!")
}

app/app.go and app/controllers.go are in the same package. They have the same path and the same name. So app/controllers.go can add methods to App.

Solution 2

You have an error because your files belong to different packages. Everything related to one struct must be in the same package.
It is possible to declare struct and its methods in different files, but they must belong same package (be in same folder).

Share:
12,075
Admin
Author by

Admin

Updated on June 19, 2022

Comments

  • Admin
    Admin almost 2 years

    How would one go about adding a method to a struct that is a different file? This is what I've tried so far but it doesn't seem to be working.

    // ./src
    package routes
    
    type App struct{
    
    }
    
    func (a *App) initializeRoutes() {
      a.Subrouter.HandleFunc("/products", a.getSomething).Methods("GET")
    }
    

    // ./src/controller
    package routes
    
    func (a *App) getSomething(w http.ResponseWriter, r *http.Request){
    
    ...
    
    }