Slice manipulation through procedure

Hi there, I’m a newbie here.
I have an issue for the following simple code, result for the State_1 is incorrect (the result is random values), whereas result for Stage_2 is correct.
Pls kindly help what is wrong with the following code, thanks.

// A slice manipulation program
package main
import "core:fmt"

State:: struct {
    aa, bb, cc, dd: int
}

route:: proc(state: State) -> []State {
    state_2: []State = {state}            //Assigning state_2 with 'state' and converting to slice 
    fmt.println("State_2: ",  state_2)   //Correct, state_2 result is 1, 0, 0, 1
    return state_2
}

main:: proc() {
    initial: State = {1, 0, 0, 1}       // Initializing the state
    state_1 := route(initial)           // Call the procedure

    fmt.println("\nState_1: ", state_1)   //This state_1 is giving a wrong result  (not being 1, 0, 0,1)
}

Short answer: you are returning stack memory in route. The compiler wasn’t clever enough to catch this case, and as such, you need to make sure not do this.

state_2: []State = {state}

is effectively sugar for the following:

state_2__array := [1]State{state}
state_2 := state_2__array[:]

From this, you should be able to tell it is using stack memory now, and thus you cannot return it from a procedure. This is because Odin has manual memory management.

2 Likes

Thanks a lot for sharing your insights.
With your suggestion, the syntax worked.
Furthermore, the procedure is also working by specifying [1] State or [dynamic]State in the ‘route’ header.

 route:: proc(state: State) -> [1]State {
    state_2: [1]State = {state}            // Assigning state_2 with 'state' and converting it to slice 
    // 
    return state_2
}

or

 route:: proc(state: State) -> [dynamic]State {
    state_2: [dynamic]State = {state}            // Assigning state_2 with 'state' and converting it to slice 
    // 
    return state_2
}

Best regards.

There is the third option:

route :: proc(state: State, dst: []State) {
    dst[0] = state
}

At a glance, this looks to me like it would have the same issue as your original snippet. I’m surprised it works. If I’m not mistaken, it’s considered a best practice to always construct a dynamic array only using the make procedure group (or the procedures make_dynamic_array, make_dynamic_array_len, and make_dynamic_array_len_cap), and I recommend you do that.

Regardless of that, I venture to guess say that returning a fixed-size array would be better in most cases.

That works because creating a [dynamic] with a compound literal will implicitly allocate using context.allocator and populate the data. This is one of the few (somewhat) implicit allocations in Odin, and so it is often avoided. There’s even a compiler flag to disallow this entirely (-no-dynamic-literals).

You may also consider using Small_Array from core:container/small_array if the number of return values might be variable (but you know what the maximum is). That is effectively just a fixed array and a number of values that should actually be used–like a [dynamic], but with the data stored inline (and thus, a fixed capacity).

1 Like