Skip to content

【Zig 日报】数组元素的可变性 #189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jiacai2050 opened this issue Apr 6, 2025 · 2 comments
Open

【Zig 日报】数组元素的可变性 #189

jiacai2050 opened this issue Apr 6, 2025 · 2 comments
Labels
FAQ 使用 Zig 时经常遇到的问题 日报 daily report

Comments

@jiacai2050
Copy link
Member

fn printNames(names: [][]const u8) void {
    for (names, 1..) |n, i| {
        std.debug.print("{d}. {s}\n", .{ i, n });
    }
}

如果一个函数的参数是切片的切片,调用是怎么写呢?一个简单的方式:

pub fn main() !void {
    const names = [_][]const u8{ "Tom", "John", "James" };
    printNames(&names);
}

zig run main.zig 后试试:

main.zig:12:16: error: expected type '[][]const u8', found '*const [3][]const u8'
    printNames(&names);
               ^~~~~~
main.zig:12:16: note: cast discards const qualifier
main.zig:4:22: note: parameter type declared here
fn printNames(names: [][]const u8) void {
                     ^~~~~~~~~~~~

一般来说,直接对数组取地址就可以转化为对应的切片,但是这里竟然报错了,说是实际传入了个指针,这是为什么呢?

这个问题产生的原因是这样的:

  • printNames 的参数是切片的切片,并且第一维度的切片是可以改变的
  • 但 names 对应的数组是 const 的,也就是说不能改变,因此有了上面的错误

一个解决方式是直接用 var 来定义 names。但这样不好,更好地做法是让 printNames 的切片参数不可变,

fn printNames(names: []const []const u8) void

改成这样就表示 names 参数不可变了。

这里容易混淆的是,切片元素的可变性是由类型决定的,比如 []const u8,而数组元素的可变性是在定义数组变量时确定的,比如 var names = ...

这里初看上去有些不一致,但确实 Andrew 深思熟虑的,大家可以参考这个 issue:Explicit use of the 'var' keyword for pointers and arrays to mutable data · Issue #5056 · ziglang/zig

加入我们

Zig 中文社区是一个开放的组织,我们致力于推广 Zig 在中文群体中的使用,有多种方式可以参与进来:

  1. 供稿,分享自己使用 Zig 的心得
  2. 改进 ZigCC 组织下的开源项目
  3. 加入微信群
@jiacai2050 jiacai2050 added the 日报 daily report label Apr 6, 2025
@jiacai2050 jiacai2050 added the FAQ 使用 Zig 时经常遇到的问题 label Apr 7, 2025
@jiacai2050
Copy link
Member Author

从类型的角度来看这个问题:

  • Slice 其实就是一种指针,而指针的指向,是有 const 之分的
  • Array 是一种基本类型,元素没有 const 之分
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const Pointer = struct {
    size: Size,
    is_const: bool,
    is_volatile: bool,
    /// TODO make this u16 instead of comptime_int
    alignment: comptime_int,
    address_space: AddressSpace,
    child: type,
    is_allowzero: bool,

    /// The type of the sentinel is the element type of the pointer, which is
    /// the value of the `child` field in this struct. However there is no way
    /// to refer to that type here, so we use `*const anyopaque`.
    /// See also: `sentinel`
    sentinel_ptr: ?*const anyopaque,

    /// Loads the pointer type's sentinel value from `sentinel_ptr`.
    /// Returns `null` if the pointer type has no sentinel.
    pub inline fn sentinel(comptime ptr: Pointer) ?ptr.child {
        const sp: *const ptr.child = @ptrCast(@alignCast(ptr.sentinel_ptr orelse return null));
        return sp.*;
    }

    /// This data structure is used by the Zig language code generation and
    /// therefore must be kept in sync with the compiler implementation.
    pub const Size = enum(u2) {
        one,
        many,
        slice,
        c,
    };
};

/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const Array = struct {
    len: comptime_int,
    child: type,

    /// The type of the sentinel is the element type of the array, which is
    /// the value of the `child` field in this struct. However there is no way
    /// to refer to that type here, so we use `*const anyopaque`.
    /// See also: `sentinel`.
    sentinel_ptr: ?*const anyopaque,

    /// Loads the array type's sentinel value from `sentinel_ptr`.
    /// Returns `null` if the array type has no sentinel.
    pub inline fn sentinel(comptime arr: Array) ?arr.child {
        const sp: *const arr.child = @ptrCast(@alignCast(arr.sentinel_ptr orelse return null));
        return sp.*;
    }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
FAQ 使用 Zig 时经常遇到的问题 日报 daily report
Projects
None yet
Development

No branches or pull requests

1 participant