Skip to content

Instantly share code, notes, and snippets.

@poutyface
Last active March 3, 2022 13:34
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 poutyface/f91ea7327d81dc40cc64b2cf36f5d168 to your computer and use it in GitHub Desktop.
Save poutyface/f91ea7327d81dc40cc64b2cf36f5d168 to your computer and use it in GitHub Desktop.
Rust Workout

Cheat Sheet

https://cheats.rs/

Use *.rs in a different directory

https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute

#[path="../some.rs"] pub mod some;

or 

pub mod some {
    include!("../some.rs");
}

main

use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    Ok(())
}

Argument Parser

    use clap::{App, Arg};

    let matches = App::new("Name")
                        .arg(Arg::with_name("file1")
                            .help("arg 1")
                            .required(true)
                            .index(1)).get_matches();

    let file_name = matches.value_of("file1").unwrap();
    println!("file name {}", file_name);

Print Type

fn print_typename<T>(_: T)
{
    println!("{}", std::any::type_name::<T>());
}

Type parameter naming

* T, U, S, V: type (2nd, 3rd...)
* I : Iterator
* F : Function type - Fn, FnMut, FnOnce
* K, V: Key, Value, HashMap<K, V>
* E : Error value type, fn a() -> E { ... Err(e) => e } 
* R : Trait Function Result value type
      fn bara<F, R>(f: F) -> R // or Result<R, Error> 
      where
        F: FnMut() -> R
* P : path, AsRef<Path> ...
* Ftu : Future 
* St : Stream

Default Binding

Discard ref / ref mut https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md https://gist.github.com/amutake/c72c634ebeee1bbe69203824bbef14d3 https://frozenlib.net/blog/2018-12-18_rust-pattern-match-2/

    let mut r: Result<i32, ()> = Ok(0);
    match &mut r {
        Ok(x) => *x = 100,
        _ => (),
    }
    assert_eq!(r.unwrap(), 100);
    
    // old
    match r {
        Ok(ref mut x) => *x = 200,
        _ => (),
    }
    assert_eq!(r.unwrap(), 200);
    
    // same
    match r.as_mut() {
        Ok(x) => *x = 300,
        _ => (),
    }
    assert_eq!(r.unwrap(), 300);

Auto Deref

    let mut v = Box::new(32);
    {
        // ref_box is &Box
        let ref_box = &v;
        // v_i32 is i32
        let v_i32 = *v;
        // &i32
        let ref_i32 = v.deref();
    }

    {
        let ref_box = &mut v;
        **ref_box += 1;
        // *ref_box type is &i32 : Box's Deref 
        // **ref_box type is *(&i32) 
    }
    assert_eq!(33, *v);

    // Pass ref variable(&v) as function argument
    // auto Deref occor
    let func = | a: &i32 | println!("{}", a);
    func(&v); // print 33

    // function call (dot operator)
    // auto Deref occor
    // v.pow() -> v.deref().pow():  v.deref() return &i32
    // 
    // v. (dot operation): 
    // Match flow &self 
    // v. -> (Box::deref(&self)) -> &i32 -> i32::pow(self, ...)
    let pv = v.pow(2);
    assert_eq!(1089, pv);

    // Cool anothor example
    let mut v = Box::new(vec![1,2,3]);
    // v[] call Box::deref_mut(because v is mut) -> return &mut Vec
    v[0] = 32;
    println!("{:?}", v); // => [32, 2, 3]

But Box is special(DerefMove)

fn main() {
    
    // boxed vec
    let x = Box::new(vec![1,2,3,4]);
    // DerefMove
    // moves the vec out into y, then deallocates the box
    // but does not call a destructor on the vec
    let y = *x;
    println!("{:?}", y);
    
    // for any other type
    let v = &vec![1,2,3,4];
    // it gives an error:
    let m = *v; // ERROR: cannot move out of borrowed content
}
fn unbox<T>(value: Box<T>) -> T {
    *value
}
struct Foo;

impl Foo {
    # this take self
    fn take(self){
        println!("take");
    }
}

fn main() {
    let a = Foo;
    let b = Box::new(a);
    b.take();
}

&mut T move samantics

via https://users.rust-lang.org/t/questions-about-mut-t-and-move-semantics-mut-t-is-move-only/37484

let x = 0;
let y = &mut x;

let z: &mut i32 = y;  // This is an implicit reborrow (equiv. to &mut *y)
let z: &mut _ = y;  // This is an implicit reborrow
let z: _ = y;  // This is a move
let z = y;  // This is a move

fn takes_mut_i32(_arg: &mut i32) {}
fn takes_mut_t<T>(_arg: &mut T) {}
fn takes_any_t<T>(_arg: T) {}

takes_mut_i32(y);  // this is an implicit reborrow
takes_mut_t(y);  // this is an implicit reborrow
takes_any_t(y);  // this is a move; sometimes this surprises people and there
                 //     have been countless "bugs" filed about it
fn moving<T>(x: T) -> T { x }

let a = 1;
moving(&mut a); 

It's same
{ a }

Rc/Arc

let arc = Arc::new(42);
// some better
// let arc_arc = arc.clone();
thread::spawn({
	// this block binds arc as same name
	let arc = arc.clone();
	move || {
		let arc = arc.clone();
	}
})

Match pattern

https://frozenlib.net/blog/2018-12-18_rust-pattern-match-2/

Function params

   struct V {
        a: i32,
    }

    let mut func = | V{a} : &mut V | *a += 1;
    let mut v = V{a: 23};
    func(&mut v);
    assert_eq!(24, v.a);

Trait Object

&dyn Trait

http://huonw.github.io/blog/2015/01/peeking-inside-trait-objects/

memory layout

Closures

Mutex

manually unlock, use std::mem::drop(MutexGuard)

let guard = mutex.lock().unwrap();
std::mem::drop(guard);

Dropping inconsistent between match and if

struct Goodbye;

impl Goodbye {
	fn new() -> Self { Goodbye }
	
	fn get(&self) -> Option<bool> {	Some(true) }
}

impl Drop for Goodbye {
	fn drop(&mut self) {
		println!("Dropping");
	}
}
fn main() {

	let x = match Goodbye::new().get() {
		Some(a) => {
			println!("In match");
			1
		},
		None => 0
	};
	
	println!("------");
	
	let y = if Goodbye::new().get() == Some(true) {
		println!("In if");
		1
	} else {
		0
    };
    
    println!("------");
	
	if let Some(a) = Goodbye::new().get() {
		println!("In if");
		1
	} else {
		0
    };

    println!("------");

    while Goodbye::new().get() == Some(true) {
        println!("In while");
        break;
    }
}

Result
In match
Dropping
------
Dropping
In if
------
In if
Dropping
------
Dropping
In while

async/await

future.await;

// expand code
let mut pinned = unsafe {
	Pin::new_unchecked(&mut future)
};
let x = loop {
	match future.poll(TLS_CONTEXT){
		Ready(x) => break x,
		Pending => yield // generator syntax
	}
};


So that, a reference of local variable in async block can across .await

async {
	let x = 5;
	future(&x).await;
}

//=> expand (Virtual code)

// async block's generator(generated struct closure) don't need to hold x's reference and Future + 'a (not 'static)
// then ref of local variable in async block pass as aurguments of nested async fn require 'static bound is compiled

// __Generator Variant have 'static lifetime
enum __Generator {
   Variant1{x:i32}
}

impl __Generator {
	fn resume() {
	   match self {
	   	Variant1(x) => {
			future(&x).await;
		}
	   }
	}
}

Pin

Pin<&mut T>
// ** Contract **
// Either T will never move again
//        OR T: Unpin

https://rust-lang.github.io/async-book/04_pinning/01_chapter.html

Unpin は, Tのmarkerで、Pinされた後での操作制御のために使われる。 Pin にした後の操作制御であって、Pinした後は、mutでunsafeしないと取れないようにするが、Pinにするまでは、保証する必要がある

  • Unpin: move していいことの意味: Pin::get_mut()で取得可能
  • !Unpin: move してはいけないことの意味、unsafe なPin::get_unchecked_mut()で取得可能

For dyn Futre

  • Pin<Box> + Send + 'static

Extend Trait

e.g ItertorExt

trait Base {
    fn base(&self),
}

trait BaseExt : Base {
    fn base_ext(&self) {
        self.base();
    }
}

impl<T: Base> BaseExt : T {}

Subtype & Variance

Great Example and Answer! https://stackoverflow.com/questions/42637911/how-can-this-instance-seemingly-outlive-its-own-parameter-lifetime

lifetime

struct Item<'a>{
    a: &'a i32,
}

struct A<'a, 'b: 'a> {
    item: &a Item<'b>,
}

https://github.com/pretzelhammer/rust-blog/blob/master/posts/translations/jp/common-rust-lifetime-misconceptions.md

lifetime of rvalue is 'static

let x: &'static = &1;

fn foo() -> 'static Foo{
  &Foo{
    ...
  }
}
'a is asummed 'static lifetime https://doc.rust-lang.org/nightly/nomicon/unbounded-lifetimes.html
fn get_str<'a>() -> &'a str;

lifetime bound

This mean that all ref lifetime of T is longer than 'a

where T + 'a

Rewrite lifetime

via crate crossbeam thread

               // Allocate `closure` on the heap and erase the `'env` bound.
                let closure: Box<dyn FnOnce() + Send + 'env> = Box::new(closure);
                let closure: Box<dyn FnOnce() + Send + 'static> =
                    unsafe { mem::transmute(closure) };

Drop

  • 通常、A<T>に対して、TのすべてのライフタイムはAよりも長くなくてはならない。
  • 例えば、Vecは, 自身のdrop時に,もしTがdropを実装している場合、T内のライフタイムも考慮する必要がある(T内にdangling pointerがあるかもしれない)。

Dropを実装していない場合

struct A<T> {
}

dropckは、Tに関連するライフタイム(Tの自体のライフタイムTのfieldに関連するライフタイム)について考慮しない(しなくていい)。

Dropを実装している場合

impl Drop for struct A<T>{
    fn drop(&mut self) {
        self.as_mut().something(); // possible dangling 
    }
}

drop関数では、すべてのライフタイムが有効である必要があるが、
drop関数の中で T を操作することを禁止しないといけない。なぜなら、Tはその時点でDropされている可能性があるため

may_dangle

* ライフタイムに関して、考慮しなくてよいとdrop-checkerに伝えるためには、#[may_dangle] を指定する.
* 下記、may_mangle は、A<T>のDropにのみ有効であって、Tにdropが実装されている場合は、チェックされる. Tを所有している場合かつTがdropを実装している場合、may_dangleは無効になる。https://users.rust-lang.org/t/phantomdata-and-dropck-confusion/26624/2
* ただし、unsafe とあるように、ユーザー任せになり、実際にTを操作することもできる(嘘ついてもいいが、動作は未定義)
  + 用はコンパイラにエラーを履かせないようにするため

unsafe impl<#[may_dangle] T> Drop for struct A<T> {
    fn drop(&mut self){
    }
}

may_mangle & PhantomData<T>

  • ドロップチェッカーがdrop時にライフタイムを気にしない場合は次の2点

    • imple<#[may_dangle] T> になっている場合
    • Strust A<T> が Tを所有していない場合(&T,*const T, *mut Tなどのポインタは所有しない、所有させるにはPhantomData)
  • may_dangle Tになっていても、A<T>がTを所有されており、Tにdropが実装されているならば、チェックされる

    • なので、Vec<T> などの、自身でメモリを確保している場合は、PhantomData<T>をフィールドに追加し、Tがdropを実装している場合、中で参照をチェックできるようにしている(ドロップチェッカーに教えている)
  • つまるところ、TがDropを実装していない場合、T がdropするとき、内部の参照を触らないので、Dropckはチェックを無視できるが、TがDropを実装している場合は、内部の参照に触る可能性がある(あってもなくても)のでDropckはこれを阻止する必要がある。AがTのraw pointerのみであると、Dropckは検知できないが、PhantomDataを置くことによって、チェックできるようにしている(may_dangleであっても)。

  • 注)may_mangleを付けなければ、Dropckはされるので、PhantomDataは必要ない。A::new()がTの生成よりも後であれば、問題なく動く。Tの生成がAよりも後にしないといけない場合は、may_mangle, PhantomDataの組み合わせが必要になる

  • https://stackoverflow.com/questions/42708462/why-is-it-useful-to-use-phantomdata-to-inform-the-compiler-that-a-struct-owns-a/42721125

  • https://users.rust-lang.org/t/phantomdata-and-dropck-confusion/26624

Strings

format!

    use std::path::Path;

    let a = 1;
    let b = "b";
    let c = "c".to_string();
    let d = Path::new("d");
    let s = format!("{} {} {} {}", a, b, c, d.display()); // => String

File read/write

    // Read
    use std::fs::File;
    use std::io::{Read};

    let mut f = File::open("./src/main.rs")?;
    let mut buf = String::new();
    f.read_to_string(&mut buf)?;
    let _a = buf.lines().map( |line| { println!("{}", line.trim()) } ).collect::<Vec<_>>();

Image

    use image;
    use image::{GenericImageView};
    use imageproc::drawing;
    use imageproc::rect::Rect;
    use orbtk::prelude::*;

    let mut img = image::open("test.jpg")?;
    // draw rectangle
    drawing::draw_filled_rect_mut(&mut img, Rect::at(10, 10).of_size(50, 50), image::Rgba([255u8, 255u8, 0u8, 255u8]));
 
    // save image
    img.save("result.png")?;
    
    // show image
    let data: Vec<u32> = img.to_rgba()
        .pixels()
        .map(|p| {
            ((p[3] as u32) << 24) | ((p[0] as u32) << 16) | ((p[1] as u32) << 8) | (p[2] as u32)
        })
        .collect();
    let image_data = Image::from_data(img.width(), img.height(), data)?;
    
    Application::new()
        .window(move |ctx| {
            Window::new()
                .title("image")
                .size(img.width(), img.height())
                // or pass file-path : .child(ImageWidget::new().image("result.png").build(ctx))
                .child(ImageWidget::new().image(image_data.clone()).build(ctx))
                .build(ctx)
        })
        .run();

lifetime

// This function say
// For caller:
// I return a reference of somthing, 
// This return value can only be used in the scope of the reference you give (lifetime 'a)
fn Bara<'a>(a: &'a str) -> &'a str {
    # something 
}

fn Bara<'a>(a: &'a str) -> Object<'a> {
    # Build Object
}

Temporary lifetimes

http://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/reference/expressions.html#temporary-lifetimes

sturct A {
    a: i32,
}

fn foo(a: &'static A){}

fn boo(){
    // temporary lifetime is promoted to 'static
    let a = &A{ a: 23 }
    foo(a)
}

T: 'a , T: 'static

T: 'a : You allow T which has some reference values

struct Bara<'a> {
    a: &'a str,
    b: Vec<&'a str>,
}

T: 'static : You want T which don't have reference Type value, except &'static variable

struct Bara {
    a: &'static str,
    b: Vec<&'static str>
}

it's ok, Because "Hello World" is &'static str type, bara.a's "'a" is 'static
struct Bara<'a>{
    a: &'a str,
}

fn main() {
    let bara = Bara {a: "Hello World" };
}

T: Fn() -> i32 + 'static : clojure (Fn) must have all variables 'static

T: Fn() -> i32 + 'a : clojure (Fn) have some reference values

Concurrent

Stream

Almost use this pattern

let a = stream::iter([iterator]).map(|x| spawn()).buffer_unordered([concurrency num]).collect::<Vec<_>>();
a.await;

Full Example

use tokio::prelude::*;
use futures::stream::{self, StreamExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>>{
    let fetches = stream::iter(0..=1000)
    .map(|x| 
        tokio::spawn(
            async move {
                println!("{} :cur thread {:?}", x, std::thread::current().id())
            }
        )
    )
    .buffer_unordered(10)  
    .collect::<Vec<_>>();
    
    // Note if you want ordered items, use buffer(10) instead of buffer_unordered 
    
    fetches.await;

    Ok(())
}

Web View

    use std::io::Write;
    use image;
    use image::png::PngEncoder;
    use image::GenericImageView;
    use base64;
    use web_view::*;

    let img_data = image::open("test.jpg")?.to_rgba();
    let (width, height) = img_data.dimensions();
    let mut encoded_image = Vec::<u8>::new();
    let encoder = PngEncoder::new(&mut encoded_image);
    encoder.encode(img_data.into_raw().as_ref(), width, height, image::ColorType::Rgba8)?;
    let img_base64 = base64::encode(&encoded_image);
    
    let html_content = format!("<html><body><h1>Hello</h1><img src=\"data:image/png;base64,{}\" \\><body></html>", img_base64);
    web_view::builder()
            .title("image")
            .content(Content::Html(html_content))
            .size(640, 480)
            .user_data(())
            .invoke_handler(|_wv, _arg| Ok(()))
            .run()
            .unwrap();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment