0

I have a use case to define a function ( in example it is "Process") which is common for two struct (Bellow example: StudentStats and EmployeeStats) and for each struct, it has its own implementation for "Pending" function.

When I run the example i get following error :

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4990ba]

What is the right approach to solve this issue ?

package main

import "fmt"

type Stats struct {
    Pending func(int, int) int
}

func (g *Stats) Process() {
    fmt.Println("Pending articles: ", g.Pending(1, 2))
}

// StatsGenerator is an interface for any entity for a case
type StatsGenerator interface {
    Process()
}

// Creating structure
type StudentStats struct {
    Stats
    name      string
    language  string
    Tarticles int
    Particles int
}

type EmployeeStats struct {
    Stats
    name      string
    Tarticles int
    Particles int
}

func (a *StudentStats) Pending(x int, y int) int {
    // Logic to identify if the accountis in pending state for stucent

    return x + y
}

func (a *EmployeeStats) Pending(x int, y int) int {
    // Logic to identify if the accountis in pending state for employe

    return x - y
}

// Main method
func main() {

    sResult := StudentStats{
        name:      "Sonia",
        language:  "Java",
        Tarticles: 1,
        Particles: 1,
    }

    eResult := EmployeeStats{
        name:      "Sonia",
        Tarticles: 1,
        Particles: 4,
    }

    var statsGenerator = []StatsGenerator{
        &sResult, &eResult,
    }
    for _, generator := range statsGenerator {
        generator.Process()

    }

}
5
  • If the common function Process calls the individual function Pending (as is in your code) you cannot easily solve this with composition. Commented Jan 14, 2021 at 16:03
  • @TehSphinX Do u have any better way to solve this ? Commented Jan 14, 2021 at 16:05
  • I am fairly sure that declaring a function in a struct like you did with Stats and Pending makes Pending a function pointer. Declaring a function you do with the syntax you used below your EmployeeStats struct. I didn't double-check that so I could be wrong. Commented Jan 14, 2021 at 16:06
  • 1
    Also an interface can declare a function but the member functions have to match. I am very surprised that you were allowed to declare pointers to EmployeeStats for your interface since those have no Process function. Commented Jan 14, 2021 at 16:09
  • 3
    Generally speaking: Go has some object-oriented features, but is not an object oriented language. Your code looks like what one would typically write when working with an object oriented language. Try to learn to think the Go way. In my now 5 years of Go I have used composition in this way maybe ~10 times altogether. Most of the time it is not worth extracting the common code. Down the line it only makes more problems when the code starts to be a little different. Go is not about typing as little as possible, it is optimised for reading instead. Commented Jan 14, 2021 at 16:14

2 Answers 2

3

Ok, here the answer I would have given.

I'd create functions to create a new StudentStat/EmployeeStat to set the Pending function correctly:

func NewStudentStats(name, language string, tarticles, particles int) *StudentStats {
    stats := &StudentStats{
        name:      name,
        language:  language,
        Tarticles: tarticles,
        Particles: particles,
    }
    // setting the correct Pending function to the Stats struct inside:
    stats.Stats.Pending = stats.Pending
    return stats
}

See the working Playground example for the full code.

Note also my comment about object oriented programming in Go on your question.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for your answer and it worked well for me. Sorry to say that I can't up-vote because I don't have enough reputation
1

The problem is that your stats type embed the Stats struct:

type StudentStats struct {
    Stats
    // [..]
}

That's all fine, but your Stats type has the Pending field, which is a function that you're calling in Process():

type Stats struct {
    Pending func(int, int) int
}

func (g *Stats) Process() {
    fmt.Println("Pending articles: ", g.Pending(1, 2))
}

But nothing ever assigns a value to Pending, so it's nil, and g.Pending(1, 2) will panic.

I'm not entirely sure what the intention of your code is, but either implement Pending() as a method on Stats, or assign a value when creating the structs:

sResult := StudentStats{
    Stats: Stats{
        Pending: func(a, b int) int {
            // Do something.
        }
    },
    // [..]
}

1 Comment

The StudentStats and ``` EmployeeStats``` have the same implementation for Process(), So i don't want to repeat the code base in the two struct. But the Pending has different implementation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.