Beet

First of all let me assert thatassert!does have a place, but its absolutely not in tests. The TLDR is that it provides a simple way to collect error locations but strikes at rust's achilles heel,compile times, and the same information can be achieve lazily through runtime backtracing.

The Bench

  • source code
  • have a playcargo install sweet-cli && sweet bench-assert --iterations 2000

Its common knowledge that even simple macros increase compile times, but did you ever wonder how much? The answer turns out to be a few milliseconds. The below benches were created by generating files withnlines of eitherassert!or a wrapperexpectfunction.

I dont consider myself a benching wizard, if you see a way this approach could be improved pleasefile an issueor pr. I'm particularly curious about what happened at the 20,000 line mark.

Implications:

For some real world context, here's some 'back of a napkin' calculations i did by grepping a few rust repos i had laying around:

Repoassert!Lines1assert!Compile TimeexpectCompile Time
bevy7,00030s0.3s
wasm-bindgen3,00015s0.15s
rust50,000250s2.5s
1

A very coarse grep ofassert!orassert_

Assert:5ms

Creating a file withnnumber of lines with anassert_eq!(n,n), calcualting how long it takes to compile the assert! macro.

LinesCompilation2Time per Line3Notes
100.21s21.00ms
1000.23s2.30ms
1,0001.54s1.54ms
2,0004.92s2.46ms
3,00011.61s3.87ms
5,00026.96s5.39ms
10,00055.00s5.50ms
20,0001.06s0.05msthis is incorrect, it actually took 10 mins

Expect:0.05ms

Creating a file withnnumber of lines with an assert! wrapper function calledexpect(n,n). This bench essentially calculates how long it takes to compile the calling of a regular rust function.

LinesCompilation2Time per Line3
100.53s53.00ms
1000.47s4.70ms
1,0000.49s0.49ms
2,0000.50s0.25ms
3,0000.53s0.18ms
5,0000.56s0.11ms
10,0000.70s0.07ms
20,0001.06s0.05ms
100,0005.37s0.05ms
500,00044.00s0.09ms
2

Compile times are retrieved from the output ofcargo build,Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs

3

Time per line is simplyline count / compile time

The Alternative - Matchers

The alternative requires both the matcher and the runner to work in unison with three rules:

  1. Theexpect()function must panic exactly one frame beneath the caller and always outputs some prefix in the payload string, in sweet this is"Sweet Error:"
  2. If the runner encounters a regular panic, just use the panics location for pretty printing.
  3. If the runner encounters a panic with the prefix, create a backtracer and use the location exactly one frame up the callstack.