# How to check if something is a string?

**URL:** <https://ziggit.dev/t/how-to-check-if-something-is-a-string/5857>\
**Category:** Help\
**Tags:** language\
**Created:** [September 2, 2024, 6:58am UTC](https://ziggit.dev/t/how-to-check-if-something-is-a-string/5857 "2024-09-02T06:58:38Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![aakashns](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/d9b06d/32.png) [@aakashns](https://ziggit.dev/u/aakashns)\
**Post date:** [September 2, 2024, 6:58am UTC](https://ziggit.dev/t/how-to-check-if-something-is-a-string/5857/1 "2024-09-02T06:58:38Z")

</div>

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:

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

```

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

---

<div class="post-metadata">

**Author:** ![tensorush](https://ziggit.dev/letter_avatar_proxy/v4/letter/t/6a8cbe/32.png) [@tensorush](https://ziggit.dev/u/tensorush)\
**Post date:** [September 2, 2024, 7:08am UTC](https://ziggit.dev/t/how-to-check-if-something-is-a-string/5857/2 "2024-09-02T07:08:38Z")

</div>

Hi @aakashns, welcome to Ziggit!

Check this reply from @dimdin:

> [@Code review: initialization via tuple of tuples](https://ziggit.dev/t/code-review-initialization-via-tuple-of-tuples/5548/2):
>
> A compile time trick. As you probably already realized, the type of a string is not []const u8 but a 0 sentinel const array of u8 with the length of the string as size. This is impossible to match as type. But I devised a trick to detect if a anytype value is an actual string. @TypeOf can have multiple arguments and returns the type that all the arguments can coerce to. fn isString(comptime v: anytype) bool { const str: []const u8 = ""; return @TypeOf(str, v) == []const u8…

Not at all a noob question, btw 😉

---

<div class="post-metadata">

**Author:** ![permutationlock](https://ziggit.dev/user_avatar/ziggit.dev/permutationlock/32/9958_2.png) [@permutationlock](https://ziggit.dev/u/permutationlock)\
**Post date:** [September 2, 2024, 7:23am UTC](https://ziggit.dev/t/how-to-check-if-something-is-a-string/5857/3 "2024-09-02T07:23:49Z")

</div>

There used to be such a function, but it and lot of other meta stuff was [removed](https://github.com/ziglang/zig/commit/d5e21a4f1a2920ef7bbe3c54feab1a3b5119bf77) 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.

---

<div class="post-metadata">

**Author:** ![aakashns](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/d9b06d/32.png) [@aakashns](https://ziggit.dev/u/aakashns)\
**Post date:** [September 2, 2024, 7:46am UTC](https://ziggit.dev/t/how-to-check-if-something-is-a-string/5857/4 "2024-09-02T07:46:52Z")

</div>

Thanks for the quick responses @tensorush and @permutationlock!

The compile time trick is elegant, although not obvious.

```zig
 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.

---

<div class="post-metadata">

**Author:** ![aakashns](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/d9b06d/32.png) [@aakashns](https://ziggit.dev/u/aakashns)\
**Post date:** [September 2, 2024, 7:50am UTC](https://ziggit.dev/t/how-to-check-if-something-is-a-string/5857/5 "2024-09-02T07:50:48Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![permutationlock](https://ziggit.dev/user_avatar/ziggit.dev/permutationlock/32/9958_2.png) [@permutationlock](https://ziggit.dev/u/permutationlock)\
**Post date:** [September 2, 2024, 7:53am UTC](https://ziggit.dev/t/how-to-check-if-something-is-a-string/5857/6 "2024-09-02T07:53:36Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![kristoff](https://ziggit.dev/user_avatar/ziggit.dev/kristoff/32/9_2.png) [@kristoff](https://ziggit.dev/u/kristoff)\
**Post date:** [September 2, 2024, 3:11pm UTC](https://ziggit.dev/t/how-to-check-if-something-is-a-string/5857/7 "2024-09-02T15:11:39Z")

</div>

The way the Zig standard library does this usually is:

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

```

So you can use it like so:

```zig
foo(u8, bar);

```

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

---

<div class="post-metadata">

**Author:** ![aakashns](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/d9b06d/32.png) [@aakashns](https://ziggit.dev/u/aakashns)\
**Post date:** [September 6, 2024, 8:27am UTC](https://ziggit.dev/t/how-to-check-if-something-is-a-string/5857/8 "2024-09-06T08:27:20Z")

</div>

@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.

```zig
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](https://github.com/ziglang/zig/commit/d5e21a4f1a2920ef7bbe3c54feab1a3b5119bf77#diff-adfee52549c345d50c3acbd67802c959e2ba7d46f7c747844035b898c8510888L405)`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).

---

<div class="post-metadata">

**Author:** ![ieh3oh](https://ziggit.dev/letter_avatar_proxy/v4/letter/i/c5a1d2/32.png) [@ieh3oh](https://ziggit.dev/u/ieh3oh)\
**Post date:** [August 29, 2026, 12:10am UTC](https://ziggit.dev/t/how-to-check-if-something-is-a-string/5857/9 "2026-08-29T00:10:45Z")

</div>

> [@permutationlock](#):
>
> You can take a look at the old implementation of `isZigString` in `meta/trait.zig` in the commit I linked.

````zig
/// Returns true if the passed type will coerce to []const u8.
/// Any of the following are considered strings:
/// ```
/// []const u8, [:S]const u8, *const [N]u8, *const [N:S]u8,
/// []u8, [:S]u8, *[:S]u8, *[N:S]u8.
/// ```
/// These types are not considered strings:
/// ```
/// u8, [N]u8, [*]const u8, [*:0]const u8,
/// [*]const [N]u8, []const u16, []const i8,
/// *const u8, ?[]const u8, ?*const [N]u8.
/// ```
pub fn isZigString(comptime T: type) bool {
    return comptime blk: {
        // Only pointer types can be strings, no optionals
        const info = @typeInfo(T);
        if (info != .Pointer) break :blk false;

        const ptr = &info.Pointer;
        // Check for CV qualifiers that would prevent coerction to []const u8
        if (ptr.is_volatile or ptr.is_allowzero) break :blk false;

        // If it's already a slice, simple check.
        if (ptr.size == .Slice) {
            break :blk ptr.child == u8;
        }

        // Otherwise check if it's an array type that coerces to slice.
        if (ptr.size == .One) {
            const child = @typeInfo(ptr.child);
            if (child == .Array) {
                const arr = &child.Array;
                break :blk arr.child == u8;
            }
        }

        break :blk false;
    };
}

````

Link: [https://codeberg.org/ziglang/zig/src/commit/994e191643f60fa2c6d6e79377340f8fad1d711b/lib/std/meta/trait.zig#L405](https://codeberg.org/ziglang/zig/src/commit/994e191643f60fa2c6d6e79377340f8fad1d711b/lib/std/meta/trait.zig#L405)

Note: This no longer compiles on 0.16.0 and newer. (Not sure when it started being broken.)

---

<div class="post-metadata">

**Author:** ![spiffyk](https://ziggit.dev/user_avatar/ziggit.dev/spiffyk/32/7202_2.png) [@spiffyk](https://ziggit.dev/u/spiffyk)\
**Post date:** [August 29, 2026, 10:34am UTC](https://ziggit.dev/t/how-to-check-if-something-is-a-string/5857/10 "2026-08-29T10:34:37Z")

</div>

> [@ieh3oh](#):
>
> This no longer compiles on 0.16.0 and newer. (Not sure when it started being broken.)

They just changed the casing of the union fields of `Type` in some previous version to be consistent with the current Zig’s naming conventions (`Array` → `array`, `Pointer` → `pointer`, etc.)

Easy fix:

````zig
/// Returns true if the passed type will coerce to []const u8.
/// Any of the following are considered strings:
/// ```
/// []const u8, [:S]const u8, *const [N]u8, *const [N:S]u8,
/// []u8, [:S]u8, *[:S]u8, *[N:S]u8.
/// ```
/// These types are not considered strings:
/// ```
/// u8, [N]u8, [*]const u8, [*:0]const u8,
/// [*]const [N]u8, []const u16, []const i8,
/// *const u8, ?[]const u8, ?*const [N]u8.
/// ```
pub fn isZigString(comptime T: type) bool {
    return comptime blk: {
        // Only pointer types can be strings, no optionals
        const info = @typeInfo(T);
        if (info != .pointer) break :blk false;

        const ptr = &info.pointer;
        // Check for CV qualifiers that would prevent coerction to []const u8
        if (ptr.is_volatile or ptr.is_allowzero) break :blk false;

        // If it's already a slice, simple check.
        if (ptr.size == .slice) {
            break :blk ptr.child == u8;
        }

        // Otherwise check if it's an array type that coerces to slice.
        if (ptr.size == .one) {
            const child = @typeInfo(ptr.child);
            if (child == .array) {
                const arr = &child.array;
                break :blk arr.child == u8;
            }
        }

        break :blk false;
    };
}

test {
    try std.testing.expectEqual(true, isZigString([]const u8));
    try std.testing.expectEqual(true, isZigString([]u8));
    try std.testing.expectEqual(true, isZigString(*const [8]u8));
    try std.testing.expectEqual(true, isZigString(*[8]u8));
    try std.testing.expectEqual(false, isZigString(struct {}));
    try std.testing.expectEqual(false, isZigString(*const u8));
    try std.testing.expectEqual(false, isZigString([]const u32));
}

const std = @import("std");

````

---

<div class="post-metadata">

**Author:** ![vulpesx](https://ziggit.dev/user_avatar/ziggit.dev/vulpesx/32/3989_2.png) [@vulpesx](https://ziggit.dev/u/vulpesx)\
**Post date:** [August 29, 2026, 11:49am UTC](https://ziggit.dev/t/how-to-check-if-something-is-a-string/5857/11 "2026-08-29T11:49:27Z")

</div>

Why don’t you check if it’s a struct first instead…
