如何编写一个可以接受和返回字符串的 Zig 函数?

问题描述 投票:0回答:1

我对 Zig 很陌生(通过其中的 Advent Of Code 进行工作),并且我对它作为函数参数和返回类型处理字符串(或者,我应该说,

[]u8
)感到非常困惑。

TL;DR 以下函数的正确实现是什么?

fn doIt(string: []u8) []u8 {
    return "prefix" ++ string;
}

第一次尝试

我希望通过以下测试:

fn doIt(string: []u8) []u8 {
    return "prefix" ++ string;
}

const expect = @import("std").testing.expect;

test {
    try expect(std.mem.eql(u8, doIt("foo"), "prefixfoo"));
}

但是

zig test
给出:

scratch.zig:27:33: error: expected type '[]u8', found '*const [3:0]u8'
    try expect(std.mem.eql(u8, doIt("foo"), "prefixfoo"));
                                    ^~~~~
scratch.zig:27:33: note: cast discards const qualifier
scratch.zig:20:17: note: parameter type declared here
fn doIt(string: []u8) []u8 {

好的,所以错误消息似乎很清楚 - 我需要更改函数的签名以接受指针。我不知道为什么我不允许直接传递~~字符串~~

[]u8
-文字,但让我们相信编译器并尝试一下:

// Let's not worry, for the moment, about the fact that we're writing a function which
// can only accept strings of length 3...
fn doIt(string: *const [3:0]u8) []u8 {
    return "prefix" ++ string;
}
...

给予

scratch.zig:23:21: error: expected type '[]u8', found '*const [9:0]u8'
    return "prefix" ++ string;
           ~~~~~~~~~^~~~~~~~~
scratch.zig:23:21: note: cast discards const qualifier
scratch.zig:20:33: note: function return type declared here
fn doIt(string: *const [3:0]u8) []u8 {
                                ^~~~

好的,两个数组指针相加得到一个指向结果的指针。这是有道理的。我一开始就不想处理指针 - 但由于我被迫进入“指针领域”,我可以理解操作的输出也将是一个指针。所以,大概我们只是使用

.*
(又名指针解引用)来返回实际值(a
[]u8
),然后呢?

fn doIt(string: *const [3:0]u8) []u8 {
    return ("prefix" ++ string).*;
}

给予

scratch.zig:21:32: error: array literal requires address-of operator (&) to coerce to slice type '[]u8'
    return ("prefix" ++ string).*;

...取址运算符如何将指针强制转换为对象?这不是该运算符所做的吗?但是好吧,让我们尝试一下...

fn doIt(string: *const [3:0]u8) []u8 {
    return &("prefix" ++ string).*;
}
scratch.zig:21:12: error: expected type '[]u8', found '*const [9:0]u8'
    return &("prefix" ++ string).*;
           ^~~~~~~~~~~~~~~~~~~~~~~
scratch.zig:21:12: note: cast discards const qualifier
scratch.zig:20:33: note: function return type declared here
fn doIt(string: *const [3:0]u8) []u8 {

...我放弃了,我一定误会了什么。谁能指出(啊哈)我正确的方向吗?

拥抱指点

采取不同的策略,如果我们将函数的返回类型更改为指针,那么前面仍然存在问题:

fn doIt(string: *const [3:0]u8) *[]u8 {
    return "prefix" ++ string;
}

const expect = @import("std").testing.expect;

test {
    try expect(std.mem.eql(u8, doIt("foo"), "prefixfoo"));
}
scratch.zig:21:21: error: expected type '*[]u8', found '*const [9:0]u8'
    return "prefix" ++ string;
           ~~~~~~~~~^~~~~~~~~
scratch.zig:21:21: note: cast discards const qualifier
scratch.zig:20:33: note: function return type declared here
fn doIt(string: *const [3:0]u8) *[]u8 {
                                ^~~~~
scratch.zig:27:32: error: expected type '[]const u8', found '*[]u8'
    try expect(std.mem.eql(u8, doIt("foo"), "prefixfoo"));
                               ~~~~^~~~~~~
/Users/scubbo/zig/zig-macos-x86_64-0.14.0-dev.2362+a47aa9dd9/lib/std/mem.zig:658:33: note: parameter type declared here
pub fn eql(comptime T: type, a: []const T, b: []const T) bool {

编译器建议我应该设置函数的返回类型

*const [9:0]u8
。经过一点调整......仍然失败,以一种更令人惊讶的方式:

fn doIt(string: *const [3:0]u8) *const [9:0]u8 {
    return "prefix" ++ string;
}

const expect = @import("std").testing.expect;

test {
    for (doIt("foo")) |char| {print("{c}", .{char});}
    print("\n", .{});
    for ("prefixfoo") |char| {print("{c}", .{char});}
    print("\n", .{});
    try expect(std.mem.eql(u8, doIt("foo"), "prefixfoo"));
}
pefixfoo
prefixfoo
1/1 scratch.test_0...FAIL (TestUnexpectedResult)
/Users/scubbo/zig/zig-macos-x86_64-0.14.0-dev.2362+a47aa9dd9/lib/std/testing.zig:546:14: 0x10846a78f in expect (test)
    if (!ok) return error.TestUnexpectedResult;
             ^
/Users/scubbo/Code/advent-of-code-2024/scratch.zig:31:5: 0x10846a936 in test_0 (test)
    try expect(std.mem.eql(u8, doIt("foo"), "prefixfoo"));
    ^
0 passed; 0 skipped; 1 failed.
error: the following test command failed with exit code 1:
/Users/scubbo/.cache/zig/o/1bb299b096246ee4dc2c6057c3d21f46/test --seed=0xc38b771a

这不是打字错误或复制粘贴错误。

return "prefix" ++ string;
输出的逐字符打印是
pefixfoo
。如果我错误地调整了数组的大小(不过,请参阅下一节),或者第一个字符由于某种原因而被删除,我也许可以理解最后一个字符会以某种方式被删除,但是什么可以使第二个角色被丢弃?

功能输入灵活

并且不考虑这样一个事实,即

(string: *const [3:0]u8) *const[9:0]u8
的函数签名可能无法接受长度为 4 的字符串。几乎不是一个多用途函数!

参考文献

我查阅过一些链接来尝试理解:

arrays string zig
1个回答
0
投票
++

运算符仅适用于具有 comptime 已知大小的数组。但您显然希望该函数在运行时完全可用。 这意味着您需要能够回答以下问题:您的函数从哪里获取新字符串的

内存

?通常,该函数将采用分配器,例如: fn doIt(allocator: std.mem.Allocator, string: []const u8) ![]u8 { const prefix = "prefix"; const new_string = try allocator.alloc(u8, prefix.len + string.len); @memcpy(new_string[0..prefix.len], prefix); @memcpy(new_string[prefix.len..], string); return new_string; }

使用完新字符串后,您需要释放它。

© www.soinside.com 2019 - 2024. All rights reserved.