How to check if something is a string?

Hi, sorry if this is a noob question, but is there an easy way to check if something is a string (i.e. its type can be coerced to []const u8)?

Here’s the function I’m looking to write:

fn isString(input: anytype) bool {
  // implementation goes here
}

I tried checking @TypeOf(input) == []const u8 but that doesn’t seem to do the job.

1 Like

Hi @aakashns, welcome to Ziggit!

Check this reply from @dimdin:

Not at all a noob question, btw :wink:

2 Likes

There used to be such a function, but it and lot of other meta stuff was removed from the standard library. You can take a look at the old implementation of isZigString in meta/trait.zig in the commit I linked.

I believe the Zig intended way to do what you are after is to just use the anytype variable as if is coercible to []const u8 and let the type system produce compilation errors when a non-string is passed. Whether those errors are always good errors is a work in progress.

3 Likes

Thanks for the quick responses @tensorush and @permutationlock!

The compile time trick is elegant, although not obvious.

 fn isString(comptime v: anytype) bool {
      const str: []const u8 = "";
      return @TypeOf(str, v) == []const u8;
  }

I believe the Zig intended way to do what you are after is to just use the anytype variable as if is coercible to []const u8

I understand, that’s what I attempted initially, but I need a way to handle both strings and struct instances, so I need to check if it is a string first, to get the code to compile.

1 Like

When using the isString function defined above with the compile time trick, isString(3) leads to a compilation error, rather than a runtime result of false. I’ll try the isZigString method instead.

2 Likes

Yes, unfotunately that trick doesn’t work as is pointed out later down in the linked thread. The isZigString method should still work though, with maybe some changes to get it to match current Zig.

1 Like

The way the Zig standard library does this usually is:

fn foo (T: type, arg: []const T) []const T;

So you can use it like so:

foo(u8, bar);

In this context bar can be any of the things that naturally coerce to a slice.

3 Likes

@kristoff I’ve seen this pattern, but I think it won’t serve my purpose, as I’m trying to define a function that can accept a string or an anonymous struct as input. E.g.

fn myFunc(input: anytype) {
    const input_type = @TypeOf(input);
    const info = @typeInfo(input_type);
    
    if (info == .Struct) {
        // do something e.g. print all fields and values
    }  else if (isZigString(input_type)) {
        // do something else e.g. print the string
    }
    @compileError("Expect struct or string. Received: " ++ @typeName(input_type));
}

Perhaps what I needed is a built-in function (called @canCoerce, for example), which can accept two types as arguments and return true or false (not a compilation error) if the two types can be coerced together.

For now, the removed isZigString function did the trick for me. I was able to generalize it to isSliceOf(T: type, Child: type) to check if something can be coerced to slice of a certain type.

Thank you all!

P.S.: I’m really enjoying using comptime. Zig is the first language where metaprogramming has just clicked, and I can write type-level logic in the same language (instead of a type system or macro syntax layered on top of the language).