Must Use Attributes
In shader programming, functions are primarily designed to calculate values rather than perform stateful side effects. If you call a pure mathematical function but discard its returned result, the entire computation is wasted. This is almost always a logical bug in your shader.
To prevent these silent bugs, WGSL provides the @must_use attribute. When applied to a function, the compiler will refuse to compile your shader if that function's returned value is discarded.
The @must_use Syntax
To declare a custom function as must-use, prepend its declaration with the @must_use attribute:
@must_use fn name(parameters) -> return_type { body }
- The
@must_useattribute is only valid on functions that explicitly declare a return_type. - If a function has no return value (a void function), adding
@must_useis a compile-time error.
Why is @must_use Safety-Critical?
On highly parallel GPUs, computational operations are extremely resource-sensitive. For example, if you calculate a coordinate projection or a normal vector transformation but fail to use the result, you waste precious clock cycles.
Moreover, discarding values often indicates that a developer forgot to write a critical line of code, such as applying a calculated translation to a vertex position. The @must_use attribute acts as an automated compiler-enforced safeguard to catch these mistakes instantly.
Native WGSL Built-ins
To protect you from common bugs, almost all of WGSL's native mathematical and vector utilities are natively annotated as @must_use. If you attempt to write a statement that contains just a built-in math call, the compiler will error out:
- Trigonometry:
sin(),cos(),tan(),asin(),acos(),atan() - Vector Math:
dot(),cross(),normalize(),length(),distance() - Arithmetic:
clamp(),min(),max(),pow(),sqrt(),abs()
What Qualifies as "Using" a Value?
The compiler is satisfied as long as the returned value is consumed or stored in one of the following ways:
1. Stored in a Variable or Constant
Saving the output directly into a memory cell:
2. Embedded in a Larger Expression
Using the output directly inside a broader calculation:
3. Used to Control Execution Flow
Utilizing the result inside a loop, conditional statement, or branch:
4. Passed as an Argument to Another Function
Nesting the function call as an input parameter:
Try it in the Playground
In the interactive playground code, we have defined a custom @must_use function calculate_critical_factor(). Notice how the commented-out statement calculate_critical_factor(2.5); would crash compilation.
Uncomment line 43 in the playground to observe the compile error directly, then fix it by assigning the result to a variable.
/*
* Copyright ©2026 Michael R. Bernstein. Licensed under Apache 2.0.
* See root README.md for global project-wide upstream attributions.
*/
// ============================================================================
// @must_use Attribute Example
// ============================================================================
// This example demonstrates how the @must_use attribute acts as a compiler-
// enforced guard to prevent developers from silently ignoring calculations.
// ============================================================================
// Pre-declaring a critical mathematical helper function.
// Prepending @must_use makes it a compile-time error to discard its return value.
@must_use
fn calculate_critical_factor(val: f32) -> f32 {
// Perform a dummy safety-critical operation
return val * 1.5 + 0.007;
}
// A standard helper function NOT marked with @must_use.
// Discarding its returned value is legally allowed, though discouraged if pure.
fn standard_calculation(val: f32) -> f32 {
return val * 0.5;
}
fn test_calls() {
// --- CASE 1: Valid Usage (Assignment) ---
// The result of our @must_use function is stored in an immutable value.
let factor = calculate_critical_factor(2.5);
// --- CASE 2: Valid Usage (Control Flow) ---
// The output is used as a direct evaluation parameter inside conditional flow.
if (calculate_critical_factor(1.0) > 1.0) {
// ...
}
// --- CASE 3: Valid Usage (Standard Return) ---
// Since standard_calculation is NOT annotated as @must_use, we are
// legally allowed to discard its result. This compiles perfectly:
standard_calculation(4.2);
// --- CASE 4: The Compile Error (Discarded Value) ---
// UNCOMMENT THE LINE BELOW to see the WGSL compiler throw an error:
// "statement has no effect; must_use value is discarded"
//
// calculate_critical_factor(2.5);
}