PyMeta - Metaprogramming Rust in Python!
PyMeta offers proc-macros to use Python as a metaprogramming language for Rust.
Currently, only CPython via PyO3 is supported, so a Python interpreter needs to be installed for this crate to work.
For now, Python >=3.14 is needed, this requirement will be relaxed to allow older Python versions soon.
See PyO3's documentation on configuring the Python version.
Nightly Rust is required for now. Nightly features will be made gated behind feature flags and/or polyfilled from stable soon.
Features
- Generate & manipulate any arbitrary Rust code from Python
- Write metaprogramming Python alongside normal Rust code
- Control code generation using any Python control flow (
for, if, match, with, functions, etc.)
- Support multiple Python implementations:
- Dev experience & IDE integration:
- Preserves all
Span (source location) information throughout the entire code generation pipeline
- Trace back from generate code to the Python expression that generated it
- Report Python exceptions with traceback at precise Rust source code locations
- 🛠️ Planned: Writing custom macros and proc-macros in Python
- 🛠️ Planned: Reusing code: Importing/exporting PyMeta Python modules from/to other Rust modules
Intro Example: A Vector Math Module
In this example, we will use PyMeta to implement part of a GLM-like vector math module.
Let's start by defining our vector structs.
pymeta! {
$for dims in range(2, 5):{
#[derive(Clone, Copy, Debug, PartialEq)]
struct Vec~$dims$ {
$for i in range(dims):{
$"xyzw"[i]$: f32,
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
struct Vec2 {
x: f32,
y: f32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
struct Vec3 {
x: f32,
y: f32,
z: f32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
struct Vec4 {
x: f32,
y: f32,
z: f32,
w: f32,
}
If you put the code above in an IDE or compile it,
notice how the compiler and IDE knows that Vec2, Vec3 and Vec4 comes from Vec~$dims$!
The compiler will report that the 3 struct are unused while pointing out Vec~$dims$,
and your IDE may show Vec~$dims$ in a gray color.
Next, let's implement some binary operation traits for our vector structs.
pymeta! {
$BINARY_OPS = [
("Add", "+"),
("Sub", "-"),
("Mul", "*"),
("Div", "/"),
("Rem", "%"),
];
$for dims in range(2, 5):{
$for op_name, op_sym in BINARY_OPS:{
$for inplace in [False, True]:{
impl std::ops::$op_name + ("Assign" if inplace else "")$ for Vec~$dims$ {
$if not inplace:{
type Output = Vec~$dims$;
}
$if not inplace:{
fn $op_name.lower()$(self, rhs: Self) -> Self {
Self {
$for d in "xyzw"[:dims]:{
$d$: self.$d$$op_sym$ rhs.$d$,
}
}
}
} $else:{
fn $f~"{op_name.lower()}_assign"$(&mut self, rhs: Self) {
$for d in "xyzw"[:dims]:{
self.$d$$op_sym + "="$ rhs.$d$;
}
}
}
}
}
}
}
}
impl std::ops::Add for Vec2 {
type Output = Vec2;
fn add(self, rhs: Self) -> Self {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl std::ops::AddAssign for Vec2 {
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
}
}
impl std::ops::Add for Vec2 {
type Output = Vec2;
fn add(self, rhs: Self) -> Self {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl std::ops::AddAssign for Vec2 {
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
}
}
impl std::ops::Sub for Vec2 {
type Output = Vec2;
fn sub(self, rhs: Self) -> Self {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
}
}
}
impl std::ops::SubAssign for Vec2 {
fn sub_assign(&mut self, rhs: Self) {
self.x -= rhs.x;
self.y -= rhs.y;
}
}
impl std::ops::Mul for Vec2 {
type Output = Vec2;
fn mul(self, rhs: Self) -> Self {
Self {
x: self.x * rhs.x,
y: self.y * rhs.y,
}
}
}
impl std::ops::MulAssign for Vec2 {
fn mul_assign(&mut self, rhs: Self) {
self.x *= rhs.x;
self.y *= rhs.y;
}
}
impl std::ops::Div for Vec2 {
type Output = Vec2;
fn div(self, rhs: Self) -> Self {
Self {
x: self.x / rhs.x,
y: self.y / rhs.y,
}
}
}
impl std::ops::DivAssign for Vec2 {
fn div_assign(&mut self, rhs: Self) {
self.x /= rhs.x;
self.y /= rhs.y;
}
}
impl std::ops::Rem for Vec2 {
type Output = Vec2;
fn rem(self, rhs: Self) -> Self {
Self {
x: self.x % rhs.x,
y: self.y % rhs.y,
}
}
}
impl std::ops::RemAssign for Vec2 {
fn rem_assign(&mut self, rhs: Self) {
self.x %= rhs.x;
self.y %= rhs.y;
}
}
impl std::ops::Add for Vec3 {
type Output = Vec3;
fn add(self, rhs: Self) -> Self {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl std::ops::AddAssign for Vec3 {
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
}
}
impl std::ops::Sub for Vec3 {
type Output = Vec3;
fn sub(self, rhs: Self) -> Self {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
}
}
}
impl std::ops::SubAssign for Vec3 {
fn sub_assign(&mut self, rhs: Self) {
self.x -= rhs.x;
self.y -= rhs.y;
self.z -= rhs.z;
}
}
impl std::ops::Mul for Vec3 {
type Output = Vec3;
fn mul(self, rhs: Self) -> Self {
Self {
x: self.x * rhs.x,
y: self.y * rhs.y,
z: self.z * rhs.z,
}
}
}
impl std::ops::MulAssign for Vec3 {
fn mul_assign(&mut self, rhs: Self) {
self.x *= rhs.x;
self.y *= rhs.y;
self.z *= rhs.z;
}
}
impl std::ops::Div for Vec3 {
type Output = Vec3;
fn div(self, rhs: Self) -> Self {
Self {
x: self.x / rhs.x,
y: self.y / rhs.y,
z: self.z / rhs.z,
}
}
}
impl std::ops::DivAssign for Vec3 {
fn div_assign(&mut self, rhs: Self) {
self.x /= rhs.x;
self.y /= rhs.y;
self.z /= rhs.z;
}
}
impl std::ops::Rem for Vec3 {
type Output = Vec3;
fn rem(self, rhs: Self) -> Self {
Self {
x: self.x % rhs.x,
y: self.y % rhs.y,
z: self.z % rhs.z,
}
}
}
impl std::ops::RemAssign for Vec3 {
fn rem_assign(&mut self, rhs: Self) {
self.x %= rhs.x;
self.y %= rhs.y;
self.z %= rhs.z;
}
}
impl std::ops::Add for Vec4 {
type Output = Vec4;
fn add(self, rhs: Self) -> Self {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
w: self.w + rhs.w,
}
}
}
impl std::ops::AddAssign for Vec4 {
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
self.w += rhs.w;
}
}
impl std::ops::Sub for Vec4 {
type Output = Vec4;
fn sub(self, rhs: Self) -> Self {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
w: self.w - rhs.w,
}
}
}
impl std::ops::SubAssign for Vec4 {
fn sub_assign(&mut self, rhs: Self) {
self.x -= rhs.x;
self.y -= rhs.y;
self.z -= rhs.z;
self.w -= rhs.w;
}
}
impl std::ops::Mul for Vec4 {
type Output = Vec4;
fn mul(self, rhs: Self) -> Self {
Self {
x: self.x * rhs.x,
y: self.y * rhs.y,
z: self.z * rhs.z,
w: self.w * rhs.w,
}
}
}
impl std::ops::MulAssign for Vec4 {
fn mul_assign(&mut self, rhs: Self) {
self.x *= rhs.x;
self.y *= rhs.y;
self.z *= rhs.z;
self.w *= rhs.w;
}
}
impl std::ops::Div for Vec4 {
type Output = Vec4;
fn div(self, rhs: Self) -> Self {
Self {
x: self.x / rhs.x,
y: self.y / rhs.y,
z: self.z / rhs.z,
w: self.w / rhs.w,
}
}
}
impl std::ops::DivAssign for Vec4 {
fn div_assign(&mut self, rhs: Self) {
self.x /= rhs.x;
self.y /= rhs.y;
self.z /= rhs.z;
self.w /= rhs.w;
}
}
impl std::ops::Rem for Vec4 {
type Output = Vec4;
fn rem(self, rhs: Self) -> Self {
Self {
x: self.x % rhs.x,
y: self.y % rhs.y,
z: self.z % rhs.z,
w: self.w % rhs.w,
}
}
}
impl std::ops::RemAssign for Vec4 {
fn rem_assign(&mut self, rhs: Self) {
self.x %= rhs.x;
self.y %= rhs.y;
self.z %= rhs.z;
self.w %= rhs.w;
}
}
Then, let's add swizzle operations to the vectors.
Since this involves a LOT of code to cover all possible arrangements, we will put them in traits and implement them for
our vectors,
to improve compile times.
pymeta! {
$import itertools;
$for in_dims in range(2, 5):{
$trait_body = rust(f~"trait Vec{in_dims}Swizzle {{}}");
$impl_body = rust(f~"impl Vec{in_dims}Swizzle for Vec{in_dims} {{}}");
$for out_dims in range(2, 5):{
$out_name = f~"Vec{out_dims}";
$for swizzle in itertools.product(*(["xyzw"[:in_dims]] * out_dims)):{
$with trait_body:{
fn $"".join(swizzle)$(self) -> $out_name$;
}
$with impl_body:{
fn $"".join(swizzle)$(self) -> $out_name$ {
$out_name$ {
$for a, b in zip("xyzw", swizzle):{
$a$: self.$b$,
}
}
}
}
}
}
}
}
trait Vec2Swizzle {
fn xx(self) -> Vec2;
fn xy(self) -> Vec2;
fn yx(self) -> Vec2;
fn yy(self) -> Vec2;
fn xxx(self) -> Vec3;
fn xxy(self) -> Vec3;
fn xyx(self) -> Vec3;
fn xyy(self) -> Vec3;
fn yxx(self) -> Vec3;
fn yxy(self) -> Vec3;
fn yyx(self) -> Vec3;
fn yyy(self) -> Vec3;
fn xxxx(self) -> Vec4;
fn xxxy(self) -> Vec4;
fn xxyx(self) -> Vec4;
fn xxyy(self) -> Vec4;
fn xyxx(self) -> Vec4;
fn xyxy(self) -> Vec4;
fn xyyx(self) -> Vec4;
fn xyyy(self) -> Vec4;
fn yxxx(self) -> Vec4;
fn yxxy(self) -> Vec4;
fn yxyx(self) -> Vec4;
fn yxyy(self) -> Vec4;
fn yyxx(self) -> Vec4;
fn yyxy(self) -> Vec4;
fn yyyx(self) -> Vec4;
fn yyyy(self) -> Vec4;
}
impl Vec2Swizzle for Vec2 {
fn xx(self) -> Vec2 {
Vec2 {
x: self.x,
y: self.x,
}
}
fn xy(self) -> Vec2 {
Vec2 {
x: self.x,
y: self.y,
}
}
}
trait Vec3Swizzle {
fn xx(self) -> Vec2;
fn xy(self) -> Vec2;
fn xz(self) -> Vec2;
fn yx(self) -> Vec2;
fn yy(self) -> Vec2;
fn yz(self) -> Vec2;
fn zx(self) -> Vec2;
fn zy(self) -> Vec2;
fn zz(self) -> Vec2;
fn xxx(self) -> Vec3;
fn xxy(self) -> Vec3;
fn xxz(self) -> Vec3;
fn xyx(self) -> Vec3;
}
Examples
Build Metadata
const BUILD_TIME: &str = pymeta! {
$from datetime import datetime;
$lit(str(datetime.now()))$
};
"2026-02-16 14:38:05.633646"
Include data from external file
#![feature(macro_metavar_expr)]
macro_rules! module_id {
($name:literal) => {
pymeta::pymeta! {
$$import json;
$$name = $name;
$$json.load(open(f~"examples/{name}.json"))["id"]$$
}
};
}
const FOO_MODULE_ID: u32 = module_id!("foo");
42
Generating Data
let GOLDEN_RATIO = pymeta!($f32((1 + 5 ** 0.5) / 2)$);
1.618034f32
pymeta! {
$from math import *;
$N = 256;
// `Token.join()` works like `str.join()`.
const SIN_TABLE: [f32; $N$] = [$Punct(',').join(sin(i / N * tau) for i in range(N))$];
}
// or with numpy:
pymeta! {
$import numpy as np;
$from math import tau;
$N = 256;
const SIN_TABLE: [f32; $N$] = [$Punct(',').join(np.sin(np.linspace(0, tau, N, endpoint=False)))$];
}
// Macro expansion:
const SIN_TABLE: [f32; 256] = [0.0, 0.024541228522912288, 0.049067674327418015, 0.07356456359966743, 0.0980171403295606, 0.1224106751992162, 0.14673047445536175, 0.17096188876030122, 0.19509032201612825, 0.2191012401568698, 0.24298017990326387, 0.26671275747489837, 0.29028467725446233, 0.3136817403988915, 0.33688985339222005, 0.3598950365349881, 0.3826834323650898, 0.40524131400498986, 0.4275550934302821, 0.44961132965460654, 0.47139673682599764, 0.49289819222978404, 0.5141027441932217, 0.5349976198870972, 0.5555702330196022, 0.5758081914178453, 0.5956993044924334, 0.6152315905806268, 0.6343932841636455, 0.6531728429537768, 0.6715589548470183, 0.6895405447370668, 0.7071067811865476, 0.7242470829514669, 0.7409511253549591, 0.7572088465064846, 0.7730104533627369, 0.7883464276266062, 0.8032075314806448, 0.8175848131515837, 0.8314696123025452, 0.844853565249707, 0.8577286100002721, 0.8700869911087113, 0.8819212643483549, 0.8932243011955153, 0.9039892931234433, 0.9142097557035307, 0.9238795325112867, 0.9329927988347388, 0.9415440651830208, 0.9495281805930367, 0.9569403357322089, 0.9637760657954398, 0.970031253194544, 0.9757021300385286, 0.9807852804032304, 0.9852776423889412, 0.989176509964781, 0.99247953459871, 0.9951847266721968, 0.9972904566786902, 0.9987954562051724, 0.9996988186962042, 1.0, 0.9996988186962042, 0.9987954562051724, 0.9972904566786902, 0.9951847266721969, 0.99247953459871, 0.989176509964781, 0.9852776423889412, 0.9807852804032304, 0.9757021300385286, 0.970031253194544, 0.9637760657954398, 0.9569403357322089, 0.9495281805930367, 0.9415440651830208, 0.9329927988347388, 0.9238795325112867, 0.9142097557035307, 0.9039892931234434, 0.8932243011955152, 0.881921264348355, 0.8700869911087115, 0.8577286100002721, 0.8448535652497072, 0.8314696123025453, 0.8175848131515837, 0.8032075314806449, 0.7883464276266063, 0.7730104533627371, 0.7572088465064847, 0.740951125354959, 0.7242470829514669, 0.7071067811865476, 0.689540544737067, 0.6715589548470186, 0.6531728429537766, 0.6343932841636455, 0.6152315905806269, 0.5956993044924335, 0.5758081914178454, 0.5555702330196022, 0.5349976198870972, 0.5141027441932218, 0.49289819222978415, 0.4713967368259978, 0.4496113296546069, 0.42755509343028203, 0.4052413140049899, 0.3826834323650899, 0.35989503653498833, 0.33688985339222033, 0.3136817403988914, 0.2902846772544624, 0.2667127574748985, 0.24298017990326407, 0.21910124015687005, 0.1950903220161286, 0.17096188876030122, 0.1467304744553618, 0.12241067519921635, 0.09801714032956083, 0.07356456359966773, 0.049067674327417966, 0.024541228522912326, 0.00000000000000012246467991473532, -0.02454122852291208, -0.049067674327417724, -0.0735645635996675, -0.09801714032956059, -0.1224106751992161, -0.14673047445536158, -0.17096188876030097, -0.19509032201612836, -0.2191012401568698, -0.24298017990326382, -0.26671275747489825, -0.2902846772544621, -0.3136817403988912, -0.3368898533922201, -0.3598950365349881, -0.38268343236508967, -0.4052413140049897, -0.4275550934302818, -0.44961132965460665, -0.47139673682599764, -0.4928981922297839, -0.5141027441932216, -0.5349976198870969, -0.555570233019602, -0.5758081914178453, -0.5956993044924332, -0.6152315905806267, -0.6343932841636453, -0.6531728429537765, -0.6715589548470184, -0.6895405447370668, -0.7071067811865475, -0.7242470829514668, -0.7409511253549589, -0.7572088465064842, -0.7730104533627367, -0.7883464276266059, -0.803207531480645, -0.8175848131515838, -0.8314696123025452, -0.8448535652497071, -0.857728610000272, -0.8700869911087113, -0.8819212643483549, -0.8932243011955152, -0.9039892931234431, -0.9142097557035305, -0.9238795325112865, -0.932992798834739, -0.9415440651830208, -0.9495281805930367, -0.9569403357322088, -0.9637760657954398, -0.970031253194544, -0.9757021300385285, -0.9807852804032303, -0.9852776423889411, -0.9891765099647809, -0.9924795345987101, -0.9951847266721969, -0.9972904566786902, -0.9987954562051724, -0.9996988186962042, -1.0, -0.9996988186962042, -0.9987954562051724, -0.9972904566786902, -0.9951847266721969, -0.9924795345987101, -0.9891765099647809, -0.9852776423889412, -0.9807852804032304, -0.9757021300385286, -0.970031253194544, -0.96377606579544, -0.9569403357322089, -0.9495281805930368, -0.9415440651830209, -0.9329927988347391, -0.9238795325112866, -0.9142097557035306, -0.9039892931234433, -0.8932243011955153, -0.881921264348355, -0.8700869911087115, -0.8577286100002722, -0.8448535652497072, -0.8314696123025455, -0.8175848131515839, -0.8032075314806453, -0.7883464276266061, -0.7730104533627369, -0.7572088465064846, -0.7409511253549591, -0.724247082951467, -0.7071067811865477, -0.6895405447370672, -0.6715589548470187, -0.6531728429537771, -0.6343932841636459, -0.6152315905806274, -0.5956993044924332, -0.5758081914178452, -0.5555702330196022, -0.5349976198870973, -0.5141027441932219, -0.49289819222978426, -0.4713967368259979, -0.449611329654607, -0.42755509343028253, -0.4052413140049904, -0.3826834323650904, -0.359895036534988, -0.33688985339222, -0.3136817403988915, -0.2902846772544625, -0.2667127574748986, -0.24298017990326418, -0.21910124015687016, -0.19509032201612872, -0.17096188876030177, -0.1467304744553624, -0.12241067519921603, -0.0980171403295605, -0.07356456359966741, -0.04906767432741809, -0.024541228522912448];
Semi-quoting
pymeta! {
$with Tokens() as signiture:{ fn say_hello(name: &str) }
trait Hello {
$signiture$;
}
struct MyStruct;
impl Hello for MyStruct {
$signiture$ {
println!("Hello {name}!");
}
}
}
trait Hello {
fn say_hello(name: &str);
}
struct MyStruct;
impl Hello for MyStruct {
fn say_hello(name: &str) {
println!("Hello {name}!");
}
}
Cursed Examples
This section contains obviously cursed fun examples that you should probably not use in actual projects.
That said, they do a good job at demonstrating the flexibility of PyMeta.
pymeta! {
$from urllib import request;
$URL = "https://raw.githubusercontent.com/shBLOCK/pymeta/refs/heads/main/src/utils/rust_token.rs";
$request.urlopen(URL).read().decode()$
}
#![feature(macro_metavar_expr)]
macro_rules! like_rust {
($($input:tt)*) => {
pymeta::pymeta! {
$$with Tokens() as input:{
$($input)*
}
$$WORDS = {"so", "like", "right", "totally", "something", "dude", "bro", "man", "just", "yo", "lol", "yeah", "uh", "um", "ah", "plz", "that", "or", "and", "then", "first", "things", "damn", "this", "thing"};
$$def process(tokens: Tokens):{
$$for token in tokens:{
$$if isinstance(token, Ident) and token.string.lower() in WORDS:{
$$continue;
}
$$if isinstance(token, Group):{
$$token.tokens = Tokens(items=process(token.tokens));
}
$$yield token;
}
}
$$Tokens(items=process(input))$$
}
};
}
like_rust! {
yeah fn this main() and then {
uh so like for i in 0..16 or something {
then just match that i {
right first things first _ if i % 3 == 0 && i % 5 == 0 => then just totally println!("FizzBuzz") yo,
then like _ if i % 3 == 0 => just println!("Fizz") plz,
and yeah _ if i % 5 == 0 => just println!("Buzz") bro,
uh and _ => then dude just println!(that damn "{i}") lol
}
}
}
}
fn main() {
for i in 0..16 {
match i {
_ if i % 3 == 0 && i % 5 == 0 => println!("FizzBuzz"),
_ if i % 3 == 0 => println!("Fizz"),
_ if i % 5 == 0 => println!("Buzz"),
_ => println!("{i}"),
}
}
}
#![feature(macro_metavar_expr)]
macro_rules! vibe {
($prompt:tt) => {
pymeta::pymeta! {
$$from openai import OpenAI;
$$client = OpenAI(base_url="http://127.0.0.1:52625/v1", api_key="");
$$response = client.chat.completions.create(
model="qwen3-it:4b",
messages=[
{"role": "system", "content": "You are a Rust code generator. Generate Rust code according to user prompt. "
+"Please ONLY generate Rust code in plain text, no explantations and other natural language. "
+"DO NOT GENERATE ANY COMMENTS (including doc comments)! "
+"Always output some code, even if the prompt is not clear or you think there's a problem with the prompt."
},
{"role": "user", "content": $prompt}
],
seed=0, stream=True
);
$$result = [];
$$for chunk in response:{
$$chunk = chunk.choices[0].delta.content;
$$if chunk:{
$$result.append(chunk);
$$print(chunk, end="", flush=True);
}
}
$$result = "".join(result);
$$"\n".join(line for line in result.splitlines() if not line.startswith("```"))$$
}
};
}
vibe!("Gimme Vec2, 3 and 4 structs with some helpful methods PLS!");
#[derive(Debug, Clone, Copy)]
pub struct Vec2 {
pub x: f64,
pub y: f64,
}
impl Vec2 {
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
pub fn zero() -> Self {
Self { x: 0.0, y: 0.0 }
}
pub fn length_squared(&self) -> f64 {
self.x * self.x + self.y * self.y
}
pub fn length(&self) -> f64 {
self.length_squared().sqrt()
}
pub fn normalize(&self) -> Self {
let len = self.length();
if len == 0.0 {
Self::zero()
} else {
Self {
x: self.x / len,
y: self.y / len,
}
}
}
pub fn dot(&self, other: &Self) -> f64 {
self.x * other.x + self.y * other.y
}
pub fn add(&self, other: &Self) -> Self {
Self {
x: self.x + other.x,
y: self.y + other.y,
}
}
pub fn subtract(&self, other: &Self) -> Self {
Self {
x: self.x - other.x,
y: self.y - other.y,
}
}
pub fn multiply_scalar(&self, scalar: f64) -> Self {
Self {
x: self.x * scalar,
y: self.y * scalar,
}
}
pub fn magnitude(&self) -> f64 {
self.length()
}
pub fn distance_to(&self, other: &Self) -> f64 {
self.subtract(other).length()
}
}
#[derive(Debug, Clone, Copy)]
pub struct Vec3 {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl Vec3 {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Self { x, y, z }
}
pub fn zero() -> Self {
Self { x: 0.0, y: 0.0, z: 0.0 }
}
pub fn length_squared(&self) -> f64 {
self.x * self.x + self.y * self.y + self.z * self.z
}
pub fn length(&self) -> f64 {
self.length_squared().sqrt()
}
pub fn normalize(&self) -> Self {
let len = self.length();
if len == 0.0 {
Self::zero()
} else {
Self {
x: self.x / len,
y: self.y / len,
z: self.z / len,
}
}
}
pub fn dot(&self, other: &Self) -> f64 {
self.x * other.x + self.y * other.y + self.z * other.z
}
pub fn add(&self, other: &Self) -> Self {
Self {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
}
}
pub fn subtract(&self, other: &Self) -> Self {
Self {
x: self.x - other.x,
y: self.y - other.y,
z: self.z - other.z,
}
}
pub fn multiply_scalar(&self, scalar: f64) -> Self {
Self {
x: self.x * scalar,
y: self.y * scalar,
z: self.z * scalar,
}
}
pub fn magnitude(&self) -> f64 {
self.length()
}
pub fn distance_to(&self, other: &Self) -> f64 {
self.subtract(other).length()
}
}
#[derive(Debug, Clone, Copy)]
pub struct Vec4 {
pub x: f64,
pub y: f64,
pub z: f64,
pub w: f64,
}
impl Vec4 {
pub fn new(x: f64, y: f64, z: f64, w: f64) -> Self {
Self { x, y, z, w }
}
pub fn zero() -> Self {
Self { x: 0.0, y: 0.0, z: 0.0, w: 0.0 }
}
pub fn length_squared(&self) -> f64 {
self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w
}
pub fn length(&self) -> f64 {
self.length_squared().sqrt()
}
pub fn normalize(&self) -> Self {
let len = self.length();
if len == 0.0 {
Self::zero()
} else {
Self {
x: self.x / len,
y: self.y / len,
z: self.z / len,
w: self.w / len,
}
}
}
pub fn dot(&self, other: &Self) -> f64 {
self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
}
pub fn add(&self, other: &Self) -> Self {
Self {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
w: self.w + other.w,
}
}
pub fn subtract(&self, other: &Self) -> Self {
Self {
x: self.x - other.x,
y: self.y - other.y,
z: self.z - other.z,
w: self.w - other.w,
}
}
pub fn multiply_scalar(&self, scalar: f64) -> Self {
Self {
x: self.x * scalar,
y: self.y * scalar,
z: self.z * scalar,
w: self.w * scalar,
}
}
pub fn magnitude(&self) -> f64 {
self.length()
}
pub fn distance_to(&self, other: &Self) -> f64 {
self.subtract(other).length()
}
}
Attributions
This crate is inspired by the great repetitive crate
by Noam2Stein.
Check it out if you want to do metaprogramming in a Rust-like language instead of Python!