How to compare strings?

Hello,
I’m tryng to compare strings in Zig but my code won’t compile.
The code is:

...
// eql is std.mem.eql
// word is of type []const u8
} else if (eql(u8, word, "GET")) {
...

The error message is:

/archive/ARCHIVE/sources/zeb/src/http.zig:41:43: error: expected type '[]u8', found '[]const u8'
                            } else if (eql(u8, word, "GET")) {

I’ve tried to use @as() to enforce u8 but it still doesn’t work

Thanks !

Small update:
I’ve tried to use @ptrCast(u8, word) and the compiler still complains. the error is:

/archive/ARCHIVE/sources/zeb/src/http.zig:41:48: error: cast discards const qualifier
                            } else if (eql(u8, @ptrCast([]u8, word), "GET")) {
                                               ^~~~~~~~~~~~~~~~~~~~
/// Compares two slices and returns whether they are equal.
pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
    if (a.len != b.len) return false;
    if (a.ptr == b.ptr) return true;
    for (a, b) |a_elem, b_elem| {
        if (a_elem != b_elem) return false;
    }
    return true;
}

Funny, I think you are using old zig compiler, because on master, eql accepts const u8 instead of u8.

https://ziglang.org/documentation/master/std/#A;std:mem.eql

1 Like

My compiler is 0.10.1 downloaded straight from the github page. I also thought it had to be “old”, because for loops (like for (things, 0..) |thing, i|) in other projects were broken.

std.mem.eql has expected a constant slice since the dawn of time, are you sure eql resolves to it and not to another local function?

I’m 100% sure. the project that i’m working on is very small at the moment and it’s easy to navigate

Update:
I still can’t figure this out. I’ve tried setting up my zig compiler again and it just won’t work. No amount of casting etc helped me.

Why can’t I just compare a variable to a string? Why is this so difficult?

Also, now I’m very VERY confused. I’ve tried to dumb down my example to something simpler, easier to tackle and the error message is even worse:

my code is:

_ = eql(u8, word, "GET");

and the error message is:

/archive/ARCHIVE/sources/zeb/src/http.zig:53:25: error: expected type '[]u8', found '[]const u8'
                        2 => {
                        ^

What is going on? Why is the compiler pointing me to a completely different part of the program, but the initial error message is the same as in the example of the original post?

Another discovery:
Normally I’d use zig build test to run all of my tests and running my project that way apparently causes the issue to occur. Without clearing the global and the local caches and running just zig build works fine - everything compiles, no errors, no warnings.

Note that the code that I’m trying to get to work is not placed is a test block

I suggest you post a complete program (a test would work the best), as minimal as possible, that reproduces your symptom. Right now we are not seeing your imports or the rest of the code, it is very difficult to guess what may be going on. Also, please include the output of zig version.

2 Likes

This is the part of my code that fails compilation

const print = @import("std").debug.print;
const tokenize = @import("std").mem.tokenize;
const eql = @import("std").mem.eql;
const parseFloat = @import("std").fmt.parseFloat;

pub const HttpMethod = enum {
    HttpMethodPost,
    HttpMethodGet,
};

pub const HttpMethodError = error{
    UnsupprtedHttpMethodError,
};

pub const HttpRequestInfo = struct {
    method: HttpMethod,
    route: []u8,
    http_version: f32,
};

pub fn parseHttp(text: []u8) !HttpRequestInfo {
    var req_info: HttpRequestInfo = undefined;

    var i: usize = 0;
    var lineno: usize = 0;
    var line: [1024]u8 = undefined;
    for (text) |char| {
        // HTTP top entry

        // eol
        if (char == '\r') {
            if (lineno == 0) {
                var it = tokenize(u8, &line, " ");
                var count: usize = 0;
                while (it.next()) |word| {
                    switch (count) {
                        // HTTP method
                        0 => {
                            if (eql(u8, word, "POST")) {
                                req_info.method = .HttpMethodPost;
                            } else if (eql(u8, word, "GET")) {
                                req_info.method = .HttpMethodGet;
                            } else {
                                return error.UnsuportedHttpMethodError;
                            }
                        },
                        // route
                        1 => {
                            req_info.route = @as([]u8, word);
                        },
                        // HTTP version
                        2 => {
                            var it2 = tokenize(u8, &word, "/");
                            var version_str = it2.next();
                            req_info.http_version = parseFloat(version_str);
                        },
                        else => {},
                    }

                    count += 1;
                }
            }

            @memset(&line, 0, @sizeOf(@TypeOf(line)));
            i += 1;
            lineno += 1;
            continue;
        }

        line[i] = char;
        i += 1;
    }

    return req_info;
}

compiled with: rm -rf ~/.cache/zig zig-out zig-cache && zig build test

full error message:

/archive/ARCHIVE/sources/zeb/src/http.zig:41:43: error: expected type '[]u8', found '[]const u8'
                            } else if (eql(u8, word, "GET")) {
                                       ~~~^~~~~~~~~~~~~~~~~
/archive/ARCHIVE/sources/zeb/src/http.zig:41:43: note: cast discards const qualifier
referenced by:
    parseHttp: /archive/ARCHIVE/sources/zeb/src/server.zig:4:38
    start: /archive/ARCHIVE/sources/zeb/src/server.zig:41:39
    remaining reference traces hidden; use '-freference-trace' to see all reference traces

error: test...
error: The following command exited with error code 1:
/archive/ARCHIVE/zig-bootstrap-0.10.1/zig-bootstrap-0.10.1/out/host/bin/zig test /archive/ARCHIVE/sources/zeb/src/server.zig --cache-dir /archive/ARCHIVE/sources/zeb/zig-cache --global-cache-dir /home/kamil/.cache/zig --name test --pkg-begin network /archive/ARCHIVE/sources/zeb/zig-network/network.zig --pkg-end --enable-cache
error: the following build command failed with exit code 1:
/archive/ARCHIVE/sources/zeb/zig-cache/o/91ee4fae979ffd9866c9825a2464d259/build /archive/ARCHIVE/zig-bootstrap-0.10.1/zig-bootstrap-0.10.1/out/host/bin/zig /archive/ARCHIVE/sources/zeb /archive/ARCHIVE/sources/zeb/zig-cache /home/kamil/.cache/zig test

zig env output:

{
 "zig_exe": "/archive/ARCHIVE/zig-bootstrap-0.10.1/zig-bootstrap-0.10.1/out/host/bin/zig",
 "lib_dir": "/archive/ARCHIVE/zig-bootstrap-0.10.1/zig-bootstrap-0.10.1/out/host/lib/zig",
 "std_dir": "/archive/ARCHIVE/zig-bootstrap-0.10.1/zig-bootstrap-0.10.1/out/host/lib/zig/std",
 "global_cache_dir": "/home/kamil/.cache/zig",
 "version": "0.10.1",
 "target": "x86_64-linux.5.10...5.10-gnu.2.31"
}

zig version output:

0.10.1

So the first thing I would say to try here is starting with this error message. You are saying it’s a u8, but the compiler is complaining that you are discarding a const qualifier. So perhaps const u8 is appropriate here. I think going one piece at a time is probably a good way to start.

It seems that the problem is a zig bug in the way it reports the error.

Your problem is that you are trying to assign const u8 that is word to route: u8 in HttpRequestInfo.
Change:

req_info.route = @as([]u8, word)

To:

req_info.route = word

and change route in the struct to const u8.

3 Likes

HUGE thanks dude!
I definitely wouldn’t have figured this out on my own

Also, be careful with storing word as the route field in HttpRequestInfo since tokenize produces slices pointing to the bytes in line; when line goes out of scope, you’ll have a slice pointing to garbage memory.

Should I store the route on the heap then (with an allocator for example)?

If you’re using an allocator, you can copy the bytes with Allocator.dupe or if you’re not allocating, you can copy the bytes with @memcpy.

2 Likes

Just so you know, most Zig projects at the moment track the master branch, and are using a completely different compiler than the one from 0.10. Unless you see in a Zig project’s readme that they are using an older version of Zig, they are probably tracking master (or they were when they last updated their code). I’d recommend downloading a more recent version of Zig than 0.10. 0.11 will be officially released soon though.

IMHO using stable version of Zig (currently 0.10) is not a good strategy at all.

Unless there’s a specific reason, going with the nightly build and adapting to the changes as they arrive is easier than migrating the code to the next release when it comes in one go.

In my opinion riding the wave is better.

This I’ve found a great resource. It even has the tip of using sliceTo for handling C strings.

3 Likes