❯ cargo new hello
Creating binary (application) `hello` package
hello/src/main.rs
fn main() {
println!("Hello, world!");
}
cargo run
❯ cd hello
❯ cargo run
Compiling hello v0.1.0 (/home/tim/tmp/hello)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.18s
Running `/home/tim/rust/target/debug/hello`
Hello, world!
❯ cargo new adder --lib
Creating library `adder` package
adder/src/lib.rs
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
cargo test
❯ cd adder
❯ cargo test
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.00s
Running unittests src/lib.rs (/home/tim/rust/target/debug/deps/adder-8c81ce131dde6a16)
running 1 test
test tests::it_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests adder
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
#[test]
, #[cfg(test)]
assert!
, assert_eq!
, &c.#[ignore]
, #[should_panic]
, &c.
#[diagnostic]
#[bench]
cargo test -- --include-ignored
#[cfg(test)]
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
cargo expand
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
pub fn add(left: usize, right: usize) -> usize {
left + right
}
cargo expand --lib --tests
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
extern crate test;
#[cfg(test)]
#[rustc_test_marker = "tests::it_works"]
pub const it_works: test::TestDescAndFn = test::TestDescAndFn {
desc: test::TestDesc {
name: test::StaticTestName("tests::it_works"),
ignore: false,
ignore_message: ::core::option::Option::None,
source_file: "src/lib.rs",
start_line: 10usize,
start_col: 8usize,
end_line: 10usize,
end_col: 16usize,
compile_fail: false,
no_run: false,
should_panic: test::ShouldPanic::No,
test_type: test::TestType::UnitTest,
},
testfn: test::StaticTestFn(
#[coverage(off)]
|| test::assert_test_result(it_works()),
),
};
fn it_works() {
let result = add(2, 2);
match (&result, &4) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(
kind,
&*left_val,
&*right_val,
::core::option::Option::None,
);
}
}
};
}
}
#[rustc_main]
#[coverage(off)]
pub fn main() -> () {
extern crate test;
test::test_main_static(&[&it_works])
}
[[test]]
name = "custom"
path = "tests/custom.rs"
harness = false
--help
--nocapture
--test-threads
#[cfg(test)]
rustc --test
#[cfg(test)]
#[cfg(feature = "test_mock_foo")]
#[test]
fn insert_just_one() {
let mut m = HashMap::new();
m.insert(42, ());
let full = m.table.buckets.iter().filter(Bucket::is_full).count();
assert_eq!(full, 1);
}
HashMap
m.table
even though it's privateRawTable
is in another modulem.table.buckets
impl RawTable {
#[cfg(test)]
pub(crate) fn buckets(&self) -> &[Bucket] {
&self.buckets
}
}
#[test]
fn insert_just_one() {
let mut m = HashMap::new();
m.insert(42, ());
let full = m.table.buckets().iter().filter(Bucket::is_full).count();
assert_eq!(full, 1);
}
struct BufWriter<T> {
#[cfg(test)]
write_through: usize,
// other fields ...
}
struct BufWriter<T> {
#[cfg(test)]
write_through: usize,
// other fields ...
}
impl<T: Write> Write for BufWriter<T> {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
// ...
if self.full() {
#[cfg(test)]
self.write_through += 1;
let n = self.inner.write(&self.buffer[..])?;
}
}
}
/// Completely frobnifies a number through I/O.
///
pub fn frobnify(i: usize) -> std::io::Result<()> {
if i == 0 {
return Ok(());
}
Err(std::io::Error::new(std::io::ErrorKind::Other, "error"))
}
/// Completely frobnifies a number through I/O.
///
/// ```
/// # use mylib::frobnify;
/// # let unfrobnified_number = 0;
/// # let already_frobnified = 1;
/// assert!(frobnify(unfrobnified_number).is_ok());
/// assert!(frobnify(already_frobnified).is_err());
/// ```
pub fn frobnify(i: usize) -> std::io::Result<()> {
if i == 0 {
return Ok(());
}
Err(std::io::Error::new(std::io::ErrorKind::Other, "error"))
}
test
is not set on mainfn main
/// # use mylib::frobnify;
/// Here's an example that uses ? on multiple types
/// and thus needs to declare the concrete error type,
/// but we don't want to distract the user with that.
/// we also hide the use that brings the function into scope.
/// ```
/// # use mylib::frobnify;
/// # let unfrobnified_number = 0;
/// frobnify("0".parse()?)?;
/// # Ok::<(), anyhow::Error>(())
/// ```
anyhow
[dev-dependencies]
anyhow = "1.0"
/// You could even replace an entire block of code completely,
/// though use this _very_ sparingly.
/// ```
/// # use mylib::frobnify;
/// # /*
/// let i = ...;
/// # */
/// # let i = 42;
/// frobnify(i)?;
/// # Ok::<(), anyhow::Error>(())
/// ```
NOTE: Use this feature with care; it can be frustrating to users if they copy-paste an example and then it doesn't work because of required steps that you've hidden.
should_panic
, ignore
, no_run
/// ```compile_fail
/// # struct MyNonSendType(std::rc::Rc<()>);
/// fn is_send<T: Send>() {}
/// is_send::<MyNonSendType>();
/// ```
rustc -W help
rust_2018_idioms
missing_docs
missing_debug_implementations
Debug
#![warn(missing_docs)]
#![deny(missing_docs)]
use proptest::prelude::*;
proptest! {
#[test]
// r"\PC*" is a regex which generates a string of arbitrary non-control characters.
fn encode_then_decode(s in r"\PC*") {
prop_assert_eq!(decode(&encode(&s)), s.to_ascii_uppercase());
}
}
#[test]
fn test_encode() {
assert_eq!(encode(""), "");
assert_eq!(encode("a"), "A");
assert_eq!(encode("hello"), "HMQXA");
assert_eq!(encode("newsletter"), "NSBPEQYNYW");
assert_eq!(encode("1 hug"), "1 HCB");
// proptest found this one!
assert_eq!(encode("z"), "Z");
}
If this approach of generating inputs until some condition is met sounds a lot like fuzzing, that's because it is— smarter people than I have argued that fuzzing is "just" property-based testing where the property you're testing for is "it doesn't crash."
Vec<Operation>
unsafe
codefn main() {
let mut x = 42;
let x: *mut i32 = &mut x;
let (x1, x2) = unsafe { (&mut *x, &mut *x) };
println!("{} {}", x1, x2);
}
❯ cargo +nightly run
Compiling use-miri v0.1.0 (/home/tim/rust/use-miri)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.34s
Running `/home/tim/rust/target/debug/use-miri`
42 42
❯ cargo +nightly miri run
Preparing a sysroot for Miri (target: x86_64-unknown-linux-gnu)... done
Compiling use-miri v0.1.0 (/home/tim/rust/use-miri)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.03s
Running `/home/tim/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo-miri runner /home/tim/rust/target/miri/x86_64-unknown-linux-gnu/debug/use-miri`
error: Undefined Behavior: trying to retag from <2377> for Unique permission at alloc1137[0x0], but that tag does not exist in the borrow stack for this location
--> src/main.rs:4:29
|
4 | let (x1, x2) = unsafe { (&mut *x, &mut *x) };
| ^^^^^^^^^^^^^^^^^^
| |
| trying to retag from <2377> for Unique permission at alloc1137[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of retag (of a reference/box inside this compound value) at alloc1137[0x0..0x4]
| errors for retagging in fields are fairly new; please reach out to us (e.g. at <https://rust-lang.zulipchat.com/#narrow/stream/269128-miri>) if you find this error troubling
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <2377> was created by a Unique retag at offsets [0x0..0x4]
--> src/main.rs:4:30
|
4 | let (x1, x2) = unsafe { (&mut *x, &mut *x) };
| ^^^^^^^
help: <2377> was later invalidated at offsets [0x0..0x4] by a Unique retag
--> src/main.rs:4:39
|
4 | let (x1, x2) = unsafe { (&mut *x, &mut *x) };
| ^^^^^^^
= note: BACKTRACE (of the first span):
= note: inside `main` at src/main.rs:4:29: 4:47
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error
$PROJECT/benches
std::hint::black_box
cargo install cargo-show-asm
pub fn vec_push() {
let mut vs = Vec::with_capacity(4);
let start = std::time::Instant::now();
for i in 0..4 {
vs.push(i);
}
println!("took {:?}", start.elapsed());
}
cargo asm vec_push
.section .text.mylib::vec_push,"ax",@progbits
.globl mylib::vec_push
.p2align 4, 0x90
.type mylib::vec_push,@function
mylib::vec_push:
.cfi_startproc
sub rsp, 104
.cfi_def_cfa_offset 112
mov rax, qword ptr [rip + __rust_no_alloc_shim_is_unstable@GOTPCREL]
movzx eax, byte ptr [rax]
call qword ptr [rip + std::time::Instant::now@GOTPCREL]
mov qword ptr [rsp + 8], rax
mov dword ptr [rsp + 16], edx
lea rdi, [rsp + 8]
call qword ptr [rip + std::time::Instant::elapsed@GOTPCREL]
mov qword ptr [rsp + 40], rax
mov dword ptr [rsp + 48], edx
lea rax, [rsp + 40]
mov qword ptr [rsp + 24], rax
mov rax, qword ptr [rip + <core::time::Duration as core::fmt::Debug>::fmt@GOTPCREL]
mov qword ptr [rsp + 32], rax
lea rax, [rip + .L__unnamed_1]
mov qword ptr [rsp + 56], rax
mov qword ptr [rsp + 64], 2
mov qword ptr [rsp + 88], 0
lea rax, [rsp + 24]
mov qword ptr [rsp + 72], rax
mov qword ptr [rsp + 80], 1
lea rdi, [rsp + 56]
call qword ptr [rip + std::io::stdio::_print@GOTPCREL]
add rsp, 104
.cfi_def_cfa_offset 8
ret
======================= Additional context =========================
.section .rodata..L__unnamed_2,"a",@progbits
.ascii "took "
.size .L__unnamed_2, 5
.section .rodata..L__unnamed_3,"a",@progbits
.byte 10
.size .L__unnamed_3, 1
.section .data.rel.ro..L__unnamed_1,"aw",@progbits
.p2align 3, 0x0
.quad .L__unnamed_2
.asciz "\005\000\000\000\000\000\000"
.quad .L__unnamed_3
.asciz "\001\000\000\000\000\000\000"
.size .L__unnamed_1, 32
black_box
use std::hint::black_box;
pub fn vec_push() {
let mut vs = Vec::with_capacity(4);
let start = std::time::Instant::now();
for i in 0..4 {
black_box(vs.as_ptr());
vs.push(i);
black_box(vs.as_ptr());
}
println!("took {:?}", start.elapsed());
}
black_box
asm.section .text.mylib::vec_push,"ax",@progbits
.globl mylib::vec_push
.p2align 4, 0x90
.type mylib::vec_push,@function
mylib::vec_push:
.cfi_startproc
.cfi_personality 155, DW.ref.rust_eh_personality
.cfi_lsda 27, .Lexception0
push r14
.cfi_def_cfa_offset 16
push rbx
.cfi_def_cfa_offset 24
sub rsp, 104
.cfi_def_cfa_offset 128
.cfi_offset rbx, -24
.cfi_offset r14, -16
mov rax, qword ptr [rip + __rust_no_alloc_shim_is_unstable@GOTPCREL]
movzx eax, byte ptr [rax]
mov edi, 16
mov esi, 4
call qword ptr [rip + __rust_alloc@GOTPCREL]
test rax, rax
je .LBB0_6
mov rbx, rax
call qword ptr [rip + std::time::Instant::now@GOTPCREL]
mov qword ptr [rsp + 56], rax
mov dword ptr [rsp + 64], edx
mov qword ptr [rsp + 8], rbx
lea rax, [rsp + 8]
#APP
#NO_APP
mov dword ptr [rbx], 0
mov qword ptr [rsp + 8], rbx
lea rcx, [rsp + 8]
#APP
#NO_APP
mov qword ptr [rsp + 8], rbx
#APP
#NO_APP
mov dword ptr [rbx + 4], 1
mov qword ptr [rsp + 8], rbx
#APP
#NO_APP
mov qword ptr [rsp + 8], rbx
#APP
#NO_APP
mov dword ptr [rbx + 8], 2
mov qword ptr [rsp + 8], rbx
#APP
#NO_APP
mov qword ptr [rsp + 8], rbx
#APP
#NO_APP
mov dword ptr [rbx + 12], 3
mov qword ptr [rsp + 8], rbx
#APP
#NO_APP
lea rdi, [rsp + 56]
call qword ptr [rip + std::time::Instant::elapsed@GOTPCREL]
mov qword ptr [rsp + 88], rax
mov dword ptr [rsp + 96], edx
lea rax, [rsp + 88]
mov qword ptr [rsp + 72], rax
mov rax, qword ptr [rip + <core::time::Duration as core::fmt::Debug>::fmt@GOTPCREL]
mov qword ptr [rsp + 80], rax
lea rax, [rip + .L__unnamed_1]
mov qword ptr [rsp + 8], rax
mov qword ptr [rsp + 16], 2
mov qword ptr [rsp + 40], 0
lea rax, [rsp + 72]
mov qword ptr [rsp + 24], rax
mov qword ptr [rsp + 32], 1
lea rdi, [rsp + 8]
call qword ptr [rip + std::io::stdio::_print@GOTPCREL]
mov esi, 16
mov edx, 4
mov rdi, rbx
call qword ptr [rip + __rust_dealloc@GOTPCREL]
add rsp, 104
.cfi_def_cfa_offset 24
pop rbx
.cfi_def_cfa_offset 16
pop r14
.cfi_def_cfa_offset 8
ret
.LBB0_6:
.cfi_def_cfa_offset 128
mov edi, 4
mov esi, 16
call qword ptr [rip + alloc::alloc::handle_alloc_error@GOTPCREL]
mov r14, rax
mov esi, 16
mov edx, 4
mov rdi, rbx
call qword ptr [rip + __rust_dealloc@GOTPCREL]
mov rdi, r14
call _Unwind_Resume@PLT
======================= Additional context =========================
.section .rodata..L__unnamed_2,"a",@progbits
.ascii "took "
.size .L__unnamed_2, 5
.section .rodata..L__unnamed_3,"a",@progbits
.byte 10
.size .L__unnamed_3, 1
.section .data.rel.ro..L__unnamed_1,"aw",@progbits
.p2align 3, 0x0
.quad .L__unnamed_2
.asciz "\005\000\000\000\000\000\000"
.quad .L__unnamed_3
.asciz "\001\000\000\000\000\000\000"
.size .L__unnamed_1, 32
let start = std::time::Instant::now();
for i in 0..1_000_000 {
println!("iteration {}", i);
my_function();
}
println!("took {:?}", start.elapsed());