Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

well, not quite since you can pass non-type things of generally any level of data type complexity (as long as it's comptime-valid, which only excludes certain types of mutation), and do stuff with them that you couldnt in c++.

    fn MyType(T: type, comptime tag: [] u8, comptime count: usize) type {
      const capitalized = some_module.capitalize(tag);
      return struct{
        fn name() []const u8 {
          return capitalized;
        }
        array: [count]T,
      };
    }
for example


That example looks easy enough to replicate in C++ with consteval + template, basically the same except a few minor syntax changes.


You absolutely can't do that in C++ with consteval + template. C++ would need support for reflection to do that, and maybe it will get it in 10 years, maybe not, but as of today this would not be possible.

Furthermore, the original argument wasn't about whether something can or can't be done in C++, it was that this one feature in Zig subsumes what would require a multitude of features from C++, such as consteval, templates, SFINAE, type traits, so on so forth...

Instead of having all these disparate features all of which work in subtly different ways, you have one single feature that unifies all of this functionality together.


You absolutely can do that in C++ with consteval + template, what's more, you don't even need consteval, constexpr will suffice:

  #include <cstdio>
  
  namespace detail {
  template<size_t N>
  struct Capitalize {
    char data[N];
    constexpr Capitalize(const char (&tag)[N]) : data{} {
      for (size_t i = 0; i < N; i++)
        data[i] = tag[i];
      data[0] &= 95;
    }
  };
  }
  
  template<typename T, size_t N, const char (&tag)[N], size_t count>
  struct MyType {
    static constexpr auto capitalized = detail::Capitalize(tag);
    constexpr auto name() const {
      return capitalized.data;
    }
    T array[count];
  };
  
  int main() {
    static constexpr char hello[] = "hello";
    MyType<int, sizeof(hello), hello, 10> value{};
    std::puts(value.name());
  }
I'd agree that zig's comptime encompasses many of C++'s features, and I appreciate the approach they took to new features by way of builtins (like @typeInfo + @Type for reflection), but this is not a good example.

Furthermore, why is type traits among the list of features that you claim is subsumed by comptime? not only is that not the case, but type traits are not so much a feature of C++ as they are of its standard library, implemented using templates (a feature of the language).


You're not wrong in general here, but C++ is going to get the core of reflection in C++26. I'm not sure enough of the details to know if it supports doing this, however.

Rust on the other hand... that might be ten years.


There are various reflection crates available on crates.io, as you know.


Yeah, I always wished that the reflect crate got further along than it has.

I still think that language support us important, but unfortunately due to what happened, I suspect that will take a long time. And that’s disappointing.


We Bevy users use Rust reflection on a daily basis, and are very happy with it :)

I agree it'd be nice if it weren't confined to our community, though.


I should check it out, I haven't had an actual use-case for reflection lately, so I haven't given bevy_reflect a try yet, but when I do, I'll make sure to give it a shot.


How does it do reflection on third party types (non-Rust, non-Bevy types)? Those that don't derive Reflect?

Don't coherence rules prevent it?


Assuming by "non-Rust types" you mean "those that the bevy_reflect crate doesn't know about", it's indeed limited by the orphan rule. That being said, bevy_reflect offers many workarounds for this problem. Because bevy_reflect is based on a type registry containing function pointers, you can actually populate it manually for types external to your crate without using the Reflect trait at all if you want to. And if your type contains fields that aren't Reflect, then you can use custom reflection logic.


non-Rust meaning non-std types, for which Bevy reflect can do manual trait implementation.


There is no reflection in this example, this is easily replicated in C++


it's not, because what you want to be a constexpr is not const. The type signature is comptime []u8, not comptime []const u8


i didn't use reflection in this example, but note that consteval shouldn't be able to do this because I mutate the string; it's not const at comptime.


I tried your example and got an error:

  error: type capture contains reference to comptime var
I'm not sure how you were suppossed to use it but here's my attempt:

  fn capitalize(tag: []u8) []u8 {
      tag[0] &= 95;
      return tag;
  }

  fn MyType(T: type, comptime tag: []u8, comptime count: usize) type {
      const capitalized = capitalize(tag);
      return struct {
          fn name() []const u8 {
              return capitalized;
          }
          array: [count]T,
      };
  }

  pub fn main() void {
      comptime var hello: [5]u8 = undefined;
      @memcpy(&hello, "hello");

      var v: MyType(i32, &hello, 10) = undefined;
      v.name();
  }
If you allow `capitalized` to be it's own instance then there's no reason to mutate the comptime parameter in the first place, and it can be replicated in C++17.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: