2
\$\begingroup\$

I'm quite new to Go and would like to improve my code. I have a representation for access levels in Go like this:

type AccessLevelValue int

const (
    GuestPermissions     AccessLevelValue = 10
    ReporterPermissions  AccessLevelValue = 20
    DeveloperPermissions AccessLevelValue = 30
    MasterPermissions    AccessLevelValue = 40
    OwnerPermission      AccessLevelValue = 50
)

What I want is a "constructor" for AccessLevelValue to which I can pass a numeric value from {10,20,30,40,50} and that returns me the according AccessLevelValue. This is my current implementation, but it seems not so nice to me:

func int2AccessLevel(accessLevel int) *AccessLevelValue {
    if accessLevel == 10 { return AccessLevel(GuestPermissions)}
    if accessLevel == 20 { return AccessLevel(ReporterPermissions)}
    if accessLevel == 30 { return AccessLevel(DeveloperPermissions)}
    if accessLevel == 40 { return AccessLevel(MasterPermissions)}
    if accessLevel == 50 { return AccessLevel(OwnerPermission)}
    return nil
}

func AccessLevel(v AccessLevelValue) *AccessLevelValue {
    p := new(AccessLevelValue)
    *p = v
    return p
}

Is there any cleaner implementation that doesn't need the if statements and returns the same result?

\$\endgroup\$
2
\$\begingroup\$

Note that unlike C enums, your const declarations define some values of AccessLevelValue, but in Go they are not considered to exhaustively define all valid values of AccessLevelValue.

Also, the values are integer constants - why are you passing around pointers to them?

You might consider a switch statement:

function int2AccessLevel(level_int int) *AccessLevel {
    switch level_int {
    case 10: return GuestPermissions;
    case 20: return ReportedPermissions;
    // ...
    default: panic("Unrecognized value for AccessLevel");
    }
}
\$\endgroup\$
  • \$\begingroup\$ thanks for the hints - the switch case is definitely an improvement. I need to pass pointers though, due to an external library and its interfaces... \$\endgroup\$ – Michael Lihs Nov 25 '17 at 12:28
1
\$\begingroup\$

As Austin Hastings mentioned, all int can be cast to AccessLevelValue.

What I would recommend you is to create a IsValid method on this type to check if the value is valid.

You may adapt the behavior of int2AccessLevel not to panic (return nil / return a pointer and ok boolean / return a pointer and an error...)

package main

import "fmt"

type AccessLevelValue int

const (
    GuestPermissions     AccessLevelValue = 10
    ReporterPermissions  AccessLevelValue = 20
    DeveloperPermissions AccessLevelValue = 30
    MasterPermissions    AccessLevelValue = 40
    OwnerPermission      AccessLevelValue = 50
)

func (a AccessLevelValue) IsValid() bool {
    switch a {
    case GuestPermissions:
    case ReporterPermissions:
    case DeveloperPermissions:
    case MasterPermissions:
    case OwnerPermission:
    default:
        return false
    }
    return true
}

func int2AccessLevel(level int) *AccessLevelValue {
    av := AccessLevelValue(level)
    if !av.IsValid() {
        panic("Provided level is not valid")
    }
    return &av
}

func main() {
    fmt.Println(int2AccessLevel(10))
    fmt.Println(int2AccessLevel(15)) // will panic
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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