forrest Jiang
forrest Jiang

Reputation: 113

How to modify function input parameters without specifying type?

I'm trying to make a function that accept 2 parameters, and set value of second parameter to first one, it might looks like(I'm not sure):

func set(a interface{}, b interface{}) {
  // do something to make a = b
}

it works like this:

a := 0
b := 10
set(&a, b)
// here a should be 10

s1 := ""
s2 := "what"
set(&s1, s2)
// here s1 should be "what"

it is expected to work on all basic types, like ints, floats, strings, bool, ...

How can I acheive this?

Upvotes: 0

Views: 557

Answers (1)

icza
icza

Reputation: 418435

This is possible using reflection:

func set(a interface{}, b interface{}) {
    reflect.ValueOf(a).Elem().Set(reflect.ValueOf(b))
}

Note that this code does not check whether a is a pointer and its element type matches the type of the value in b. Also note that using reflection is slower and you lose compile-time type safety. The above code is much slower than assigning a single int value to an int variable or assigning a string value to a string variable.

Testing it:

a := 0
b := 10
set(&a, b)
fmt.Println(a)

s1 := ""
s2 := "what"
set(&s1, s2)
fmt.Println(s1)

Output (try it on the Go Playground):

10
what

Here's a version that adds checks to avoid runtime panics:

func set(a interface{}, b interface{}) error {
    v1 := reflect.ValueOf(a)
    if v1.Kind() != reflect.Ptr {
        return errors.New("a must be pointer")
    }
    if v1.IsZero() {
        return errors.New("a must not be a nil pointer")
    }

    v1 = v1.Elem()
    v2 := reflect.ValueOf(b)
    if v1.Type() != v2.Type() {
        return errors.New("a's element type must match b's type")
    }

    if !v1.CanSet() {
        return errors.New("unsettable value in a")
    }

    v1.Set(v2)
    return nil
}

Testing it:

a := 0
b := 10
err := set(a, b)
fmt.Printf("set(a, b): a=%v, err: %v\n", a, err)
err = set(&a, b)
fmt.Printf("set(&a, b): a=%v, err: %v\n", a, err)
err = set((*int)(nil), b)
fmt.Printf("set((*int)(nil), b): a=%v, err: %v\n", a, err)

s1 := ""
s2 := "what"
err = set(&s1, s2)
fmt.Printf("set(&s1, s2): s1=%v, err: %v\n", s1, err)
err = set(&s1, b)
fmt.Printf("set(&s1, s2): s1=%v, err: %v\n", s1, err)

Output (try it on the Go Playground):

set(a, b): a=0, err: a must be pointer
set(&a, b): a=10, err: <nil>
set((*int)(nil), b): a=10, err: a must not be a nil pointer
set(&s1, s2): s1=what, err: <nil>
set(&s1, s2): s1=what, err: a's element type must match b's type

Upvotes: 4

Related Questions