6 ms·
Zig doesn't have macros, it has functions which can be run at comptime. You can make a function that returns a type and call it from another function. All decla
by pfg_ 2y ago
Zig doesn't have macros, it has functions which can be run at comptime. You can make a function that returns a type and call it from another function. All declarations are only analyzed when they are first used, and functions when called at comptime are memoized based on their arguments. The order of evaluation is really simple and predictable.
- nukem222 2y ago> Zig doesn't have macros, it has functions which can be run at comptime You raised my hopes and dashed them quite expertly, sir. Bravo!
- gliptic 2y agoYou're probably underestimating what you can do with these.
- weinzierl 2y agoJust for completeness: Rust has functions which can be run at comptime as well. They are called const fn and Rust has them out of the box, no crate required. They are also true Rust and not macros with a separate syntax. They are still not an adequate substitute for Zig's comptime feature. For one and in a sense they are much more limited than comptime functions in Zig but for another (and for better or worse) they also have much higher aspirations than Zig. const fn must always be able to be run at compile time or run time and always produce bit-identical results. This is much harder than it looks at first glance because it must also uphold in a cross-compiling scenario where the compile time environment can be vastly different from the run time environment. This requirement also forbids any kind of side effect, so Rust const fn are essentially pure functions and I've heard them called like that.
- kzrdude 2y agoRust has compile time environment variables and const fns can parse them. It's one very nice and easy way to experiment with or configure rust code at compile time, and should be explored more.
- Zambyte 2y agoReading environment variables contradicts my mental model of pure functions. Are they just not pure functions then? Or pure-ish?
- kzrdude 2y agoThe functions are pure but they take an input from a built-in (looks like a macro) that reads the environment variable at compile time. Also, you only compile once, so how could you tell the difference? You could say - if it was using const fn that it's a "templated" function that depends on compile time settings.
- Zambyte 2y agoI see, I guess if you can't set environment variables during build time that makes it pure-ish enough.
- vlovich123 2y agoYou can set environment variables in your build.rs / the user sets it like A=b cargo build.
- dwattttt 2y agoThere's two lookups that can occur, distinguishing them makes it clearer. Looking up the value of an environment variable at runtime is not a const operation, and produces an error if you try to do it in a const fn. Looking up the value of an environment variable during compile time _can_ be done in a const context, but it'll only happen once. The environment should be considered an input to a const fn, and that makes it "pure". EDIT: These two operations can both be done in non-const functions too, they're different functions (well, one's a macro).
- johnisgood 2y agoSo if Rust has Zig's comptime feature, what is this crate? How does it differ, what does it add?
- IshKebab 2y agoRust doesn't have Zig's comptime feature. Rust's const fn's are normal functions that are capable of running at compile time. It's an optional optimisation; it doesn't exist any additional semantic capabilities because they also need to be able to run at runtime. Zig's comptime functions only run at compile time, so they can do extra things - in particular manipulating types - that you can't do if your function needs to run at runtime. (Don't mention dependent types.)
- johnisgood 2y agoOkay, so this crate adds Zig's actual comptime?
- weinzierl 2y agoNo, it doesn't because it is based on Rust macros which are strictly less capable than Zig comptime in a crucial way (compile time reflection). Neither Rust macros nor const fn are 100% what Zig comptime is but they have other properties that Zig comptime lacks. Apples and oranges.
- johnisgood 2y agoThen the title is misleading (although not surprising). So what are the major differences between the two anyways? Just to be sure.
- zozbot234 2y agoNote that dependently-typed code also effectively "runs at compile-time", it's inherent to that programming model. You can "extract" an ordinary program from dependently-typed code which you can then compile to a binary and run as usual, but then that program will not feature dependent types in their full generality.
- IshKebab 2y agoYeah I don't really understand why Rust copied that from C++, where constexpr functions only might run at compile time. C++ ended up having to add consteval and constinit which really are compile-time.
- kibwen 2y agoIt's the other way around. Rust has always had contexts that are guaranteed to be compile-time (const and static items), and gradually added the ability to run some subset of the language at compile-time (const fn) specifically to accommodate const/static items (e.g. to replace the old lazy_static with the modern LazyLock), and naturally also allows these functions to run at runtime if you want (and in what would otherwise be a runtime context, you can enforce compile-time evaluation with a const block).
- tialaramex 2y agoHuh? What is it you think Rust copied here? I agree that the choice in C++ is essentially worthless, so that in practice you can write functions which are definitely never executed at compile time and aren't constant in any sense, label them constexpr and that compiles anyway. It just becomes yet more noise C++ programmers learn to type by reflex to get the correct behaviour from their compiler, joining explicit. But in Rust that's not what you're getting. Rust's const fn is none of the options C++ decided it needed, Rust says if the parameters are themselves constants then we promise we can evaluate this at compile time and if appropriate we will -- this means we can use Rust's const fn where we'd use C++ consteval, but the function can also be called at runtime with variable parameters - and we can use Rust's const where we'd use C++ constinit, calling these const fn with constant parameters. Because Rust is more explicit about safety of course, we can often get away with claiming some value is "constant" in C++ despite actually figuring out what it is at runtime, and Rust isn't OK with that, for example in my code const OVERSIZE: u32 = SIG_BITS.next_power_of_two() << 1; We can just calculate what power of two is bigger than SIG_BITS and shift it left at compile time. But... pub(super) static SHORT_80: LazyLock<Rational> = LazyLock::new(|| Rational::fraction(1, 80).unwrap()); The Rational type is a big rational, it owns heap allocations so we'll just make one once, at runtime, and then re-use it whenever we need this particular fraction (it's for calculating natural logarithms of arbitrary computable real numbers).
- tyilo 2y agoFloats are not guaranteed to be bit-identical at compile time and run time in Rust.
- weinzierl 2y agoLast time I checked the float functions that have no bit-identical results (mostly transcendental functions) were missing from Rust's const fn for exactly that reason.
- kibwen 2y agoIt's not quite as bad as it sounds, because the only difference is that the representation of NaN (the sign and the payload bits) isn't guaranteed to be stable. If you're not relying on any specific representation of NaN, then floating-point math in const fn is identical, and observed differences would be considered a soundness bug in the Rust compiler.
- weinzierl 2y agoI had to look this up because it is a while that I tried to use floating point math in a const fn and it seems that the differences you described have been decided to be acceptable. Looks like floating point math in const fn is coming. Here is the respective tracking issue: https://github.com/rust-lang/rust/issues/128288 https://github.com/rust-lang/rust/issues/128288
- kibwen 2y agoIt's actually already here, it stabilized last year in 1.82: https://blog.rust-lang.org/2024/10/17/Rust-1.82.0.html#floating-point-nan-semantics-and-const https://blog.rust-lang.org/2024/10/17/Rust-1.82.0.html#float...
- weinzierl 2y agoOh, nice. Sometime I find it really hard to track the status of new Rust features. For example the tracking issue I linked is still open with "Stabilize" missing.
- WhyNotHugo 2y agoWhat Rust is missing is reflection and the ability to define types and functions via code. Zig's comptime is often used for this: to generate code (for example, a serialiser for a type) or to generate types (generics being a typical thing, but lots of other usages are viable).
- vlovich123 2y agoYou can define types and functions via code via macros. For example, [1] which creates a new sibling type and injects a ::builder method into your type. And you can add reflection [2]. So if you can add what you need via crates, is the language actually missing it or is it just not as ergonomic / performant as it needs to be or is it an education problem? [1] https://docs.rs/typed-builder/latest/typed_builder/ https://docs.rs/typed-builder/latest/typed_builder/ [2] https://docs.rs/reflect/latest/reflect/ https://docs.rs/reflect/latest/reflect/
- Asraelite 2y ago> They are still not an adequate substitute for Zig's comptime feature. For one and in a sense they are much more limited than comptime functions in Zig The syntactic restrictions don't really matter; it's still Turing-complete. The key difference is that types are values in Zig but not in Rust, which is a core design feature of the language and can't be changed easily.