Skip to content

Instantly share code, notes, and snippets.

@DanielKeep
Last active November 20, 2015 07:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DanielKeep/325960a142c0925a3da5 to your computer and use it in GitHub Desktop.
Save DanielKeep/325960a142c0925a3da5 to your computer and use it in GitHub Desktop.
prospective r-l.o examples
*.sublime-workspace

Rules

  • All code must fit in an area of 69×22 cells.

  • Each example must begin (or very nearly so) with:

    // This code is editable and runnable!

  • All examples must run on the playpen (and by extension, the rust-lang.org front page).

  • Examples should avoid external crates, unless otherwise impossible.

// This code is editable and runnable!
#![feature(libc)]
extern crate libc;
extern "C" {
// Bind directly to C functions, including varargs.
fn atoi(str: *const u8) -> libc::c_int;
fn printf(format: *const u8, ...) -> libc::c_int;
}
fn main() {
// All FFI is potentially unsafe.
unsafe {
let s = format!("{}\0", 42);
// Rust strings are UTF-8 (ptr, len); C strings are pointers
// with terminating `\0`s. We need to convert between them.
let s_ptr = s.as_bytes().as_ptr();
// Beyond that, there is no overhead to calling C functions.
let i = atoi(s_ptr);
printf(b"`%s` (0x%x) -> %d\n\0".as_ptr(), s_ptr, s_ptr, i);
}
}
// This code is editable and runnable!
fn main() {
let a = &7;
println!("inner result: {}", inner(a));
}
// Returning pointers from functions is 100% safe in Rust.
fn min_ref<'x>(a: &'x i32, b: &'x i32) -> &'x i32 {
if *a < *b { a } else { b }
}
// Lifetime elision handles the annotations in this simple case. The
// lifetime of the output is assumed to be the same as the input.
fn inner(a: &i32) -> &i32 {
let b = &4;
let c = min_ref(a, b);
println!("min ref result: {}", *c);
// return c;
// ^ ERROR: borrowed value does not live long enough
// This is because `&4` only exists inside this function.
return a;
}
// This code is editable and runnable!
// Note that circumventing the stdlib is still an unstable feature.
#![feature(lang_items, start, no_std, libc, core_slice_ext)]
#![no_std]
extern crate libc;
extern "C" {
fn printf(fmt: *const libc::c_char, ...) -> libc::c_int;
}
// Entry point for this program
#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
unsafe {
printf(b"Hello, World!\n\0".as_ptr() as *const _);
0
}
}
// Functions required by the compiler, normally provided by libstd.
#[lang = "eh_personality"] extern fn eh_personality() {}
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
// This code is editable and runnable!
fn main() {
let s = String::from("an owned, heap-allocated string");
{
let substr = &s["an owned, ".len()..];
println!("substr: {:?}", substr);
// let new_variable = s;
// ^ ERROR: cannot move out of `s` because it is borrowed
// Borrow of `s` ends here, which means...
}
// ...we can now move the contents of `s`.
let new_variable = s;
println!("new_variable: {:?}", new_variable);
// let yet_another_s = s;
// ^ ERROR: use of moved value: `s`
// Move semantics plus borrow checking prevents moving values
// that are still in use *and* unnecessary copying.
}
// This code is editable and runnable!
fn thing(n: i32) -> (i32, i32) {
// Patterns are everywhere! You can deconstruct on let bindings:
let (i, j) = (2*n, n/2);
(i + j, i - j)
}
// Argument bindings can pattern match, too:
fn some((a, b): (i32, i32)) -> Option<i32> {
match a - b {
// Patterns in `match` can have guard expressions:
x if x < 0 => None,
x => Some(x)
}
}
fn main() {
// There are also "refutable" matches, like `if let`:
if let Some(x) = some(thing(42)) {
println!("{}", x);
}
}
{
"folders":
[
{
"path": "."
}
],
"settings":
{
"rulers": [69]
}
}
// This code is editable and runnable!
use std::ops::Add;
// Even if this generic is in a library or *never* instantiated, the
// compiler can perform type checking and verify the code is correct.
fn add<T>(a: T, b: T) -> T
where T: Add<Output=T> {
a + b
}
// fn bad_add<T>(a: T, b: T) -> T { a + b }
// ^ ERROR: binary operation `+` cannot be applied to type `T`
// The generic function itself fails type checking, not at usage.
fn main() {
let _a: _ = add(3, 5i32);
let _b: u8 = add(b'0', 3);
let _c: _ = add(5.23f32, 0.2);
// let _d = add("hello,", " world!");
// ^ ERROR: trait `Add` is not implemented for the type `&str`
// This error happens at usage, not within the generic function.
}
// This code is editable and runnable!
fn main() {
let s = "5 2 9";
// The type of `nums` depends on the type of the closure, which
// depends on the result of `parse` which... we don't know yet.
let mut nums = s.split_whitespace().map(|w| w.parse().unwrap());
// The type of `a`, `b`, and `c` *also* depend on the result of
// the `parse` calls, which we still don't know.
let a = nums.next().unwrap();
let b = nums.next().unwrap();
let c = nums.next().unwrap();
// The compiler can now infer that `0`, `a`, `b`, and `c` *must*
// be the same type. Fallback permits it to assume this type is
// `i32`, which allows the compiler to infer all previously
// unspecified types. We have to "hint" that we want a `Vec`
// here, since many types can be `collect()`ed into.
let result: Vec<_> = (0..a).map(|e| e * b + c).collect();
println!("{:?}", result);
}
// cargo-deps: crossbeam="0.1.6"
// This code is editable and runnable!
extern crate crossbeam;
fn main() {
// Mutate a table *on the stack* across multiple threads, without
// data races or use-after-free, checked at compile time.
let mut table = [0; 100];
crossbeam::scope(|scope| {
// Safely split the table into disjoint sub-slices.
for (i, slice) in table.chunks_mut(10).enumerate() {
// Spawn a thread within this scope, using the subslice.
scope.spawn(move || {
for e in slice { *e = i }
});
}
// `crossbeam::scoped` makes sure all threads will join
// before leaving this scope.
});
println!("{:?}", &table[..]);
}
// This code is editable and runnable!
use std::sync::{Arc, Mutex};
fn main() {
let mut a = Box::new(42); // `a` gets *moved* into the thread.
let x = std::thread::spawn(move || {
*a = *a * 2; a // mutate a, then move it back out
});
// Mutate an array shared between multiple threads.
let bs = Arc::new(Mutex::new([0, 0, 0]));
let mut ys = vec![];
for is in vec![vec![1, 2], vec![2, 0, 2], vec![1, 0, 2, 1]] {
let bs = bs.clone(); // create new `Arc` handle.
ys.push(std::thread::spawn(move || {
for i in is { bs.lock().unwrap()[i] += 1; }
}));
}
let a = x.join().expect("thread panicked");
for y in ys { y.join().expect("thread panicked"); }
println!("a: {:?}, bs: {:?}", a, bs);
}
// This code is editable and runnable!
fn main() {
// Put an `i32` on the heap, then *move* it into the thread.
// There are no copies or sharing going on here.
let mut a = Box::new(0);
let x = std::thread::spawn(move || {
for i in 0..100_000 { *a += i }
a // pass the mutated `a` back to the main thread.
});
// Also works for more complex types like dynamic strings.
let mut b = String::new();
let y = std::thread::spawn(move || {
for _ in 0..200 { b.push_str("wasting cycles") }
b
});
// Join the two threads, moving the results back.
let a = x.join().expect("thread panicked");
let b = y.join().expect("thread panicked");
println!("a: {:?}, b end: {:?}", a, &b[b.len()-11..]);
}
// This code is editable and runnable!
use std::sync::{Arc, Mutex};
fn main() {
// `Arc` gives us thread-safe shared ownership, `Mutex` gives us
// thread-safe mutation. Rust keeps us from getting this wrong.
let ns = Arc::new(Mutex::new([0, 0, 0]));
let mut ts = vec![]; // for thread join handles.
// Increment "random" elements of ns in multiple threads.
for idxs in vec![vec![1, 2], vec![2, 0, 2], vec![1, 0, 2, 1]] {
let ns = ns.clone(); // clone a new `Arc` handle.
// Keep thread value so we can join it later.
ts.push(std::thread::spawn(move || {
// We *cannot* forget to: lock the mutex, unlock the
// mutex, or escape a pointer to the data. 100% safe.
for idx in idxs { ns.lock().unwrap()[idx] += 1; }
}));
}
for t in ts { t.join().expect("thread panicked"); }
println!("ns: {:?}", ns);
}
// This code is editable and runnable!
fn main() {
// Create a closure. There's no heap allocation involved.
let closure = || 2 + 2;
// In fact, with no captures, it requires no memory *at all*.
println!("size of closure: {}", std::mem::size_of_val(&closure));
println!("static result: {}", two_times_static(&closure));
println!("dynamic result: {}", two_times_dynamic(&closure));
}
// `f()` can be inlined, eliminating abstraction cost.
fn two_times_static<F: Fn() -> i32>(f: &F) -> i32 {
2 * f()
}
// Or, we can dispatch at runtime using the same interface.
fn two_times_dynamic(f: &Fn() -> i32) -> i32 {
2 * f()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment