Yet Another Rust Util (yaru) ๐ ๏ธ
A lightweight collection of utilities for Rust applications, providing:
- โฑ๏ธ Relative Timestamping: Track elapsed time since application start
- ๐งต Thread Identification: Debug multi-threaded and async code with thread IDs
- ๐ Memory Inspection: Visualize memory layout of Rust types (addresses, lengths, capacities, reference counts, lock states)
- ๐๏ธ SQL Result Formatting: Pretty-print any
Serializequery result as a UTF-8 table
yaru is designed for developers and educators who need to understand timing, concurrency, memory models, and database results without heavy frameworks.
๐ฆ Installation
Add this to your Cargo.toml:
[]
= "0.2"
โฑ๏ธ Time Logging
Print vs Format
Each logging variant has two versions:
- Print (
t_print!,nt_print!,ti_print!,nti_print!): Output directly to stdout - Format (
t_format!,nt_format!,ti_format!,nti_format!): Return formattedString
| Variant | Thread ID | Leading Newline | Use Case |
|---|---|---|---|
t* |
No | No | Standard timestamped logging |
nt* |
No | Yes | Visual phase separation |
ti* |
Yes | No | Multi-threaded/async debugging |
nti* |
Yes | Yes | Thread ID + phase separation |
Example
use *;
Output:
[01][00:000] ==> Starting main
[02][00:001] Worker thread running...
[00:002] All done.
[00:003] Formatted at runtime
Functions and Macros Available
Each variant has both function and macro versions, in both print and format styles:
Print to stdout:
t_print(msg)/t_print!(...)nt_print(msg)/nt_print!(...)ti_print(msg)/ti_print!(...)nti_print(msg)/nti_print!(...)
Return String:
t_format(msg)/t_format!(...)nt_format(msg)/nt_format!(...)ti_format(msg)/ti_format!(...)nti_format(msg)/nti_format!(...)
Use init_time_log() to manually reset the timer origin.
๐ Memory Inspection
Visualize memory layout of Rust types using macros or functions.
Print vs Format
| Operation | Output to stdout | Returns String |
|---|---|---|
| Macro | print_*_ptr! |
format_*_ptr! |
| Function | print_*_ptr() |
format_*_ptr() |
Supported Types
| Type | Print Macro | Format Macro | Print Function | Format Function |
|---|---|---|---|---|
| Universal | print_ptr! |
format_ptr! |
โ | โ |
String |
print_string_ptr! |
format_string_ptr! |
print_string_ptr() |
format_string_ptr() |
&str |
print_str_ptr! |
format_str_ptr! |
print_str_ptr() |
format_str_ptr() |
&[T] |
print_slice_ptr! |
format_slice_ptr! |
print_slice_ptr() |
format_slice_ptr() |
Box<T> |
print_box_ptr! |
format_box_ptr! |
print_box_ptr() |
format_box_ptr() |
Vec<T> |
print_vec_ptr! |
format_vec_ptr! |
print_vec_ptr() |
format_vec_ptr() |
Rc<T> |
print_rc_ptr! |
format_rc_ptr! |
print_rc_ptr() |
format_rc_ptr() |
Arc<T> |
print_arc_ptr! |
format_arc_ptr! |
print_arc_ptr() |
format_arc_ptr() |
&T |
print_ref_ptr! |
format_ref_ptr! |
print_ref_ptr() |
format_ref_ptr() |
Mutex<T> |
print_mutex_ptr! |
format_mutex_ptr! |
print_mutex_ptr() |
format_mutex_ptr() |
RwLock<T> |
print_rwlock_ptr! |
format_rwlock_ptr! |
print_rwlock_ptr() |
format_rwlock_ptr() |
HashMap |
print_hashmap_ptr! |
format_hashmap_ptr! |
print_hashmap_ptr() |
format_hashmap_ptr() |
โก Super-Powered Macros (Time + Inspect)
You can combine Time Logging and Memory Inspection in a single macro call!
Simply prefix any inspection macro with t_, nt_, ti_, or nti_.
Syntax: [prefix]_[macro]!
| Prefix | Effect | Example |
|---|---|---|
t_ |
Timestamped inspection | t_print_vec_ptr!(v) |
nt_ |
Newline + Timestamped inspection | nt_print_vec_ptr!(v) |
ti_ |
Thread ID + Timestamped inspection | ti_print_vec_ptr!(v) |
nti_ |
Newline + Thread ID + Timestamped inspection | nti_print_vec_ptr!(v) |
This works for ALL inspection macros:
t_print_ptr!nti_print_string_ptr!ti_print_rc_ptr!- ...and so on.
Basic Example
use *;
Output:
s String::{ addr: 0x7ff..., len: 5, cap: 5 }
my_string String::{ addr: 0x7ff..., len: 5, cap: 5 }
s String::{ addr: 0x7ff..., len: 5, cap: 5 }
Smart Pointer Example
use *;
use Arc;
Output:
arc Arc::{ addr: 0x5fa..., strong_count: 2, weak_count: 0 }
arc2 Arc::{ addr: 0x5fa..., strong_count: 2, weak_count: 0 }
Lock State Example
use *;
use Mutex;
๐๏ธ SQL Result Formatting
Pretty-print any &[T] (where T: Serialize) as a UTF-8 table. Works with sqlx, SeaORM, Diesel, or any struct that derives Serialize.
Three macros are available โ all automatically capture the variable name via stringify!, so there is no manual name parameter:
| Macro | Output |
|---|---|
print_sql_table! |
Pretty UTF-8 table only |
print_sql_json_table! |
Debug dump + table |
print_sql_json! |
Debug dump only |
print_sql_table! โ Table Only
use Serialize;
let users = vec!;
print_sql_table!;
Output:
=> TABLE: users (2)
โโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโ
โ id โ name โ email โ
โโโโโโชโโโโโโโโชโโโโโโโโโโโโโโโโโโโโก
โ 1 โ Alice โ alice@example.com โ
โโโโโโผโโโโโโโโผโโโโโโโโโโโโโโโโโโโโค
โ 2 โ Bob โ bob@example.com โ
โโโโโโดโโโโโโโโดโโโโโโโโโโโโโโโโโโโโ
print_sql_json_table! โ Debug + Table
The most complete debug view: raw Debug representation and a clean table.
use Serialize;
let tasks = vec!;
print_sql_json_table!;
Output:
=> JSON: tasks (2)
[
Task { id: 1, title: "Write tests", done: true },
Task { id: 2, title: "Review code", done: false },
]
=> TABLE: tasks (2)
โโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโ
โ id โ title โ done โ
โโโโโโชโโโโโโโโโโโโโโชโโโโโโโก
โ 1 โ Write tests โ T โ
โโโโโโผโโโโโโโโโโโโโโผโโโโโโโค
โ 2 โ Review code โ F โ
โโโโโโดโโโโโโโโโโโโโโดโโโโโโโ
print_sql_json! โ Debug Only
use Serialize;
let tasks = vec!;
print_sql_json!;
Output:
=> JSON: tasks (2)
[
Task { id: 1, title: "Write tests", done: true },
Task { id: 2, title: "Review code", done: false },
]
NULL Handling โ Option Fields
Ideal for LEFT JOIN results where some columns may be NULL:
use Serialize;
let results = vec!;
print_sql_table!;
Output:
=> TABLE: results (2)
โโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโโโโ
โ user_id โ user_name โ bio โ avatar_url โ
โโโโโโโโโโโชโโโโโโโโโโโโชโโโโโโโโโชโโโโโโโโโโโโโก
โ 1 โ Alice โ Hello! โ NULL โ
โโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโโโโโค
โ 2 โ Bob โ NULL โ NULL โ
โโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโ
1:N Relations โ Tuples with Nested Vec
Works with SeaORM's find_with_related() results (Vec<(Parent, Vec<Child>)>):
// SeaORM example
let result: = find
.find_with_related
.all.await?;
print_sql_json_table!;
// Parent fields are flattened + children summarised in extra columns
Smart Value Formatting
| Rust type | Rendered as |
|---|---|
String / &str |
Plain text |
bool |
T / F |
Option::None |
NULL |
Option::Some(v) |
v |
Vec<T> (of objects) |
[name1, name2, ...] (extracts name or title field) |
| Other | JSON representation |
โ ๏ธ Important: Macro Usage Best Practices
Macros Automatically Borrow - Don't Use &!
All pointer inspection macros internally add & to the value. Pass values directly, without &.
This is one of the most important rules when using this library's macros:
use *;
let v = vec!;
// โ
CORRECT: Macro handles borrowing
print_ptr!; // Shows address of v
print_vec_ptr!; // Shows address of v's heap buffer
print_vec_ptr!;
// โ WRONG: Double reference, shows TEMPORARY reference address
print_ptr!; // Creates &&v internally โ shows temporary!
print_ref_ptr!; // Shows address of TEMPORARY &v, not v!
Why This Matters
When you write print_ptr!(&v), the macro adds another &, creating &&v. This double reference points to a temporary location on the stack, not the actual data you're trying to inspect. The output will be misleading and won't help you understand your program's memory layout.
The Simple Rule: Pass the value directly to macros, without &. The macro handles borrowing for you.
Detailed Examples by Type
โ Vectors - Correct Usage
let v = vec!;
print_vec_ptr!; // โ
Shows v's heap buffer address
print_ptr!; // โ
Shows v's heap buffer address
โ Vectors - Incorrect Usage
let v = vec!;
print_vec_ptr!; // โ Shows temporary reference address
print_ptr!; // โ Shows temporary reference address
โ Box - Correct Usage
let b = Boxnew;
print_box_ptr!; // โ
Shows heap allocation address
print_ptr!; // โ
Shows heap allocation address
โ Box - Incorrect Usage
let b = Boxnew;
print_box_ptr!; // โ Shows temporary reference address
โ References - Correct Usage
let x = 42;
print_ptr!; // โ
Shows address of x on stack
// If you need to inspect a reference itself:
let r = &x;
print_ref_ptr!; // โ
Shows where x lives (r is already a reference)
โ References - Incorrect Usage
let x = 42;
print_ptr!; // โ Shows temporary reference, not x's address!
Functions Require Explicit Reference
When using functions directly (not macros), you must provide the reference:
use *;
let v = vec!;
// Function signature: print_vec_ptr(&Vec<T>, prefix: &str)
print_vec_ptr; // โ
Explicit & required
// Macro signature: print_vec_ptr!(value) or print_vec_ptr!(value, prefix)
print_vec_ptr!; // โ
No & needed, macro handles it
print_vec_ptr!; // โ
No & needed, macro handles it
Summary Table
| You want to... | Use this | Note |
|---|---|---|
| Print with auto-label | print_vec_ptr!(v) |
No & needed |
| Print with custom label (macro) | print_vec_ptr!(v, "label") |
No & needed |
| Print with custom label (fn) | print_vec_ptr(&v, "label") |
& required for fn |
| Get formatted String (macro) | format_vec_ptr!(v) |
No & needed |
| Get formatted String (fn) | format_vec_ptr(&v, "label") |
& required for fn |
Quick Reference
Macros (most common):
- โ
print_ptr!(value) - โ
print_vec_ptr!(value) - โ
print_box_ptr!(value) - โ
print_rc_ptr!(value) - โ
print_ptr!(&value)โ DON'T DO THIS
Functions (when needed):
- โ
print_vec_ptr(&value, "label") - โ
format_vec_ptr(&value, "label")
๐ฏ Design Philosophy
- Lightweight Dependencies: Only
serde,serde_json, andcomfy-table. - Educational Focus: Clear, verbose output for learning.
- Non-Invasive: All functions/macros only borrow data.
- Database-Agnostic: Works with any ORM/query library โ sqlx, SeaORM, Diesel, etc.
- Consistent API:
print_*for output,format_*for strings.
๐ Full Documentation
See the API documentation on docs.rs for complete details.
โ๏ธ License
Licensed under either of:
at your option.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.