Skip to content

Discussion: Array/Slice typechecking #568

@Hejsil

Description

@Hejsil

One of the best simple changes Zig gives us over C is the ability to have actual arrays/slices (not just pointers) in our code. However, after playing with these arrays, I found that the type checking did not make sense to me. I hope that this issue will either:

  • Explain exactly what the rules are for slices and arrays in the type system (Hopefully even get this information in the documentation).
  • Or, if it needs to change, discuss what should change.

Ok, so let me explain why I have a problem understanding the type system behind arrays and slices. First, the documentation doesn't seem to explain the use of const in types such as pointers and slices. This means, I kinda have to go by what I think it means, which is: []const i64 means a slice, whose elements cannot be changed or a slice of const i64.
With this in mind, let's go use arrays and slices.

// A slice, whose elements can be changed
var slice: []i64 = undefined;

// A slice, whose elements can't be changed
var sliceOfConstElements: []const i64 = undefined;

// An array, whose elements can be changed
var arrayOfSize: [4]i64 = undefined;

// An array, whose elements can't be changed
// NOTE: This will not compile.
// var arrayOfSizeWithConstElements: [4]const i64 = undefined; // error: const qualifier invalid on array type

slice[0] = 1;
sliceOfConstElements[0] = 1; // error: cannot assign to constant
arrayOfSize[0] = 1;
// arrayOfSizeWithConstElements[0] = 1;

Ok, so far so good. I seems I'm not totally misunderstanding it since sliceOfConstElements[0] = 1; gave an error. I wonder why we can't have arrays of constant elements though...

Next, we ofcourse want to be able to assign our variables to values, instead of undefined:

var slice: []i64 = []i64{ 1, 2, 3, 4 }; // error: expected type '[]i64', found '[4]i64'
var sliceOfConstElements: []const i64 = []i64{ 1, 2, 3, 4 };
var arrayOfSize: [4]i64 = []i64{ 1, 2, 3, 4 };

Ooh, so I can't assign a slice to an array literal? But I can if the slice is const? Why? My best guess is, that apparently [4]i64 actually implicitly is const. Why not call the type [4]const i64?
Well, ok. Let's test something then. Can I declare const in the literal?

var slice: []i64 = []const i64{ 1, 2, 3, 4 }; // error: expected type '[]i64', found '[4]i64'
var sliceOfConstElements: []const i64 = []const i64{ 1, 2, 3, 4 };
var arrayOfSize: [4]i64 = []const i64{ 1, 2, 3, 4 };

I can? Ooook? So what is the difference between []const i64{ 1, 2, 3, 4 } and []i64{ 1, 2, 3, 4 }?

// Test 1/1 Array literal types...reached unreachable code
test "Array literal types" {
    if (@typeOf([]i64{ 1, 2, 3 }) == @typeOf([]const i64{ 2, 4, 6 })) {
        unreachable;
    }
}

No difference? So I guess the compiler does not respect const in array literals.

I could go on with my story format like this, but I think you all get that this is confusing me a little. Here is a bigger piece of code, that outlines more things that confuse me:

fn useSlice(slice: []i64) { }
fn useSliceConst(slice: []const i64) { }

pub fn main() -> %void {
    var   varArray        = []i64 { 1, 2, 3 };
    const constArray      = []i64 { 1, 2, 3 };
    var   varArrayConst   = []const i64 { 1, 2, 3 };
    const constArrayConst = []const i64 { 1, 2, 3 };

    var   varArrayExplicit       : []i64 = []i64 { 1, 2, 3 }; // error: expected type '[]i64', found '[3]i64'. Why?
    const constArrayExplicit     : []i64 = []i64 { 1, 2, 3 }; // error: expected type '[]i64', found '[3]i64'. Why?
    var   varArrayConstExplicit  : []const i64 = []i64 { 1, 2, 3 }; // No error? Why?
    const constArrayConstExplicit: []const i64 = []i64 { 1, 2, 3 }; // No error? Why?

    var   varArrayExplicitLiteralConst       : []i64 = []const i64 { 1, 2, 3 }; // error: expected type '[]i64', found '[3]i64'
    const constArrayExplicitLiteralConst     : []i64 = []const i64 { 1, 2, 3 }; // error: expected type '[]i64', found '[3]i64'. Ok, so we can see here, that the const declartion does not make the type const
    var   varArrayConstExplicitLiteralConst  : []const i64 = []const i64 { 1, 2, 3 };
    const constArrayConstExplicitLiteralConst: []const i64 = []const i64 { 1, 2, 3 };

    // 
    // Reassign
    //
    varArray        = []i64 { 2, 4, 6 };
    constArray      = []i64 { 2, 4, 6 }; // error: cannot assign to constant
    varArrayConst   = []i64 { 2, 4, 6 }; // No error? Why? Does the compile no respect the const in []const i64{ ... }?
    constArrayConst = []i64 { 2, 4, 6 }; // error: cannot assign to constant

    // I guess these all make sense
    varArray        = []i64 { 2, 4 }; // error: expected type '[3]i64', found '[2]i64'
    constArray      = []i64 { 2, 4 }; // error: cannot assign to constant
    varArrayConst   = []i64 { 2, 4 }; // error: expected type '[3]i64', found '[2]i64'
    constArrayConst = []i64 { 2, 4 }; // error: cannot assign to constant

    varArray        = []const i64 { 2, 4, 6 }; // No error? Why?
    constArray      = []const i64 { 2, 4, 6 }; // error: cannot assign to constant
    varArrayConst   = []const i64 { 2, 4, 6 };
    constArrayConst = []const i64 { 2, 4, 6 }; // error: cannot assign to constant

    // I guess these all make sense
    varArray        = []const i64 { 2, 4 }; // error: expected type '[3]i64', found '[2]i64'
    constArray      = []const i64 { 2, 4 }; // error: cannot assign to constant
    varArrayConst   = []const i64 { 2, 4 }; // error: expected type '[3]i64', found '[2]i64'
    constArrayConst = []const i64 { 2, 4 }; // error: cannot assign to constant

    //
    // Assign element
    // 
    varArray       [0] = 5;
    constArray     [0] = 5; // error: cannot assign to constant. Why? I guess const declared arrays have const elements?
    varArrayConst  [0] = 5; // No error? Why?
    constArrayConst[0] = 5; // error: cannot assign to constant

    //
    // Func taking non const element slice
    //
    useSlice(varArray); // error: expected type '[]i64', found '[3]i64'. Why no implicit convertion to slice? 
    useSlice(constArray); // error: expected type '[]i64', found '[3]i64'. If implicit convertion happend here, this probably still shouldn't work, because of the implicit const elements.
    useSlice(varArrayConst); // error: expected type '[]i64', found '[3]i64'. This should still give error after an implicit convertion to slice.
    useSlice(constArrayConst); // error: expected type '[]i64', found '[3]i64'. This should still give error after an implicit convertion to slice.
    useSlice([]i64{ 1, 2, 3 }); // error: expected type '[]i64', found '[3]i64'. Should this be allowed if we have implicit slice convertion?
    useSlice([]const i64{ 1, 2, 3 }); // error: expected type '[]i64', found '[3]i64'. This probably shouldn't.

    //
    // Func taking const element slice
    //
    // So if we mark a func to take const element slices, then we get the implicit convertion to slice always?
    useSliceConst(varArray);
    useSliceConst(constArray);
    useSliceConst(varArrayConst);
    useSliceConst(constArrayConst);
    useSlice([]i64{ 1, 2, 3 }); // No error? Isn't this a type missmatch?
    useSlice([]const i64{ 1, 2, 3 });
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions