Black Lives Matter. Support the Equal Justice Initiative.

The Go Blog

Rust language

bantana
15 April 2020

Expression 表达式

Place Expression, 位置表达式, LeftValue, 左值

 位置表达式 就是 表示内存地址的表达式。通常包含一个name和内存地址.

1. 本地变量
2. 静态变量
3. 解引用 (*expr)
4. 数组索引 (expr[expr])
5. 字段引用 (expr.field)
6. 位置表达式组合

 通过位置表达式可以对某个数据单元的内存进行读写.主要是进行写操作,这也是位置表达式可以被赋值的原因.

位置表达式 通常在作用域范围内有一个name和一个内存地址.从语义来看,位置表达式在作用域范围内有持久的状态.

Value Expression, 值表达式, RightValue, 右值

 值表达式一般是临时数据.值表达式要么是字面量,要么是表达式求值过程中创建的临时值.

例如:

字面量:
    "hello,world"
    123
    
&p p变量的的内存地址

1+2

值在内存中只是一个内存中的内容而已,比如 "hello,world" , 只是存放在某个内存地址的内容;你不能对内容进行写操作,也就是你不能对一个不是内存地址的地方写操作.

go:

var x string = "hello, world";
x := "hello, world";
这在go中表示,在内存中有一个内存区域的内容是"hello, world",把他的地址赋给x;

c:

int x = 0;
int* p = &x;  // p是一个指针类型,指向的数据类型是int, p中存放的是x的地址值(&x);
*p = 5;  // *p 是一个指向p中存放地址的指针. 所以是位置表达式,可以被赋值;

但是

&x = 3; // [clangd] Expression is not assignable [Error]
&x 是取x变量的地址值,并不代表指向这个地址的指针.所以他只能是一个RightValue.

变量的所有权

let x = "hello".to_string();
let <x: LV> = <"hello".to_string(): RV>;
let <LV> = <RV>
LV = RV
RV是内存中的一个真实内存区域的值,这个RV的所有权的管理问题;

lv1 = <RV>
lv2 = <RV>
...
lvn = <RV>

如果RV被其中一个修改,或删除,那么其他位置访问这个RV就有可能出各种未期望的状况;
我们知道一个值都是读取操作没有什么安全问题;
但是我们想限制只有一个人拥有写这个内存区域的所有权.

看一个所有权move后的访问错误:

let x2 = "hello".to_string();
// println!("addr: {:p}, value: {}", &x2, x2);
let y2 = x2;
// println!("addr: {:p}, value: {}", &y2, y2);

println!("addr: {:p}, value: {}", &x2, x2);

// error[E0382]: borrow of moved value: `x2`
//   --> src/main.rs:32:39
//    |
// 27 |     let x2 = "hello".to_string();
//    |         -- move occurs because `x2` has type `std::string::String`, which does not implement the `Copy` trait
// 28 |     println!("addr: {:p}, value: {}", &x2, x2); // addr: 0x7fff752ee650, value: hello
// 29 |     let y2 = x2;
//    |              -- value moved here
// ...
// 32 |     println!("addr: {:p}, value: {}", &x2, x2);
//    |                                       ^^^ value borrowed here after move
//
// error: aborting due to previous error
//
// For more information about this error, try `rustc --explain E0382`.

解释下编译器发现的问题;

27行单独执行没什么问题;
29行接着执行也没什么问题;这个只是把x2对<RV: "hello".to_string();>的所有权move给了y2, 这时候x2和他的RV都被析构掉了;
32行这时候访问了&x2; 得到一个错误 你在x2 所有权move后,使用了 &x2 试图访问,在这之前这个x2和他的RV都不存在了;
这时候回到27行错误提示:
编译器认为你如果试图使用32行的代码,除非27行这个x2的type实现了'Copy'的trait;

在这个页面底部有一个实现这个 'Copy' trait 的例子, 看后面的example: ex1.rs

通常语言的设计者会根据实际使用情况来给一些常规类型实现默认Copy的trait;

比如 ex2.rs 例子中实现了i32类型的默认Copy trait.

fn main() {
    let x: i32 = 8;
    let y: i32 = x;
    println!("y {}", y);
    println!("x {}", x);
}
// y 8
// x 8

传值(COPY) or 传引用

传值的特点就是值COPY一份,

传引用就是传一个指针地址,

理解这个问题的重心是理解你要对数据做什么处理,要如何更高效或更有效的使用数据;

如果需要对数据做修改处理,但是要保留一份原始数据,就需要使用 值COPY, 如果只是需要对数据做读取处理,可以放心的使用引用, 如果需要对原始数据做修改处理,可以选择引用.这个需要注意的是会不会引起其他问题.

传值(COPY)

1. 原始变量和内容会被复制一份,内部生成同名但是在函数体作用域有效;
2. 有些复杂结构体内部会使用指针,为避免错误修改原数据,需要对数据做 "深COPY";
当然这样做是有代价的,你需要评估 costs (内存开销,性能问题).

传引用(传递本身)

1. 传引用开销很小,通常就是一个指针长度.
2. 只是对数据的读取操作,应该优先使用这种方式,可以理解为只读引用;
3. 如果需要对原数据修改就需要认真评估这个问题了.因为可能存在数据竞态和数据竞争的问题.;

 竞态指多个并发处理对相同的内存区域(临界区)出现"修改"行为。

 数据竞争是指一个在写这个内存区域,而另一个在读这个内存区域,这两者就处于数据竞争行为。

Shadowing

// Shadowing
fn main() {
    let y = 2;
    println!("addr: {:p}, value: {}", &y, y); // addr: 0x7ffe8322e8dc, value: 2
    let y = 3;
    println!("addr: {:p}, value: {}", &y, y); // addr: 0x7ffe8322e95c, value: 3

}

在这个例子中隐藏了几个问题:

1. let 是不可变,那么为什么可以第二次使用 let redefine? 我们知道在一些语言中const是不能 redefine 的.为什么rust允许;这个问题要看设计者如何评估;

一种设计思路是禁止这样使用,简化,并避免这种隐藏的坑;
一种思路是我认为你可以这么使用,前提是你知道这么做是在干嘛.

很多老语言的设计者可能就没考虑过这个问题.

2. rust中不可变变量 y 的地址和内容在第2次 使用 [let y = 3;] 时候都被更改了,这种就叫做shadowing, 也就是第一个 y 的地址和内容都被Shadowing(屏蔽?)了,那原来的y地址和内存中的内容块是怎么处理的呢? 我没有深究这个,但你可以考虑下如何设计解决这个问题,

方案:

1. 编译器在编译的过程中加入了一段代码,在rebinding y (let y = 3;)之前,先加入一段针对之前 y 的析构代码;
2. 某些类型系统底层可以扩展;
3. 不管,如果是stack变量,等stack退出的时候自动清除?如果包含有heap的分配怎么办?只能依赖某些memory leak分析工具;

example

ex1.rs

struct Count {
    data: i32,
    value: i32,
}

impl Clone for Count {
    fn clone(&self) -> Count {
        return Count {
            data: self.data,
            value: self.value,
        };
    }
}

impl Copy for Count {}

fn main() {
    let mycount = Count {
        data: 10,
        value: 22,
    };
    let youcount = mycount;
    println!("mycount addr: {:p}", &mycount);
    println!("youcount addr: {:p}", &youcount);
    println!("mycount.data addr: {:p} {}", &mycount.data, mycount.data);
    println!("youcount.data addr: {:p} {}", &youcount.data, youcount.data);
    println!("mycount.value addr: {:p} {}", &mycount.value, mycount.value);
    println!(
        "youcount.value addr: {:p} {}",
        &youcount.value, youcount.value
    );
    //
}

// mycount addr: 0x7ffd69a07308
// youcount addr: 0x7ffd69a07310
// mycount.data addr: 0x7ffd69a07308 10
// youcount.data addr: 0x7ffd69a07310 10
// mycount.value addr: 0x7ffd69a0730c 22
// youcount.value addr: 0x7ffd69a07314 22

简单的也可以使用编译器指令 #[derive(Copy, Clone)] 来帮我们自动实现Copy, Clone 的 trait

#[derive(Copy, Clone)]
struct Count {
    data: i32,
    value: i32,
}

fn main() {
    let mycount = Count {
        data: 10,
        value: 22,
    };
    let youcount = mycount;
    println!("mycount addr: {:p}", &mycount);
    println!("youcount addr: {:p}", &youcount);
    println!("mycount.data addr: {:p} {}", &mycount.data, mycount.data);
    println!("youcount.data addr: {:p} {}", &youcount.data, youcount.data);
    println!("mycount.value addr: {:p} {}", &mycount.value, mycount.value);
    println!(
        "youcount.value addr: {:p} {}",
        &youcount.value, youcount.value
    );
}
/*
    Borrowing and Ownership in Rust
*/

/*
  Stack
  - Extremely Fast
  - Values must have Fixed Sizes
  - Always puts data in on Top
  - data pushed down as new data comes in
*/

/*
  Heap
  - Less Organized and Slower
  - Accepts Dynamicly Sized Data or Data that can Grow
  - Returns a Pointer which goes on the stack
  - Pointer points to where the data is on the Heap
*/

/*
Ownership Rules:
1) Each value has a variable which is its owner.
2) There can only be one owner at any given time.
3) When the owner goes out of scope, the value will be dropped out of memory.
*/

/*
Borrowing Rules:
    1) Allowed infinite borrows for readonly access.
    2) Readonly borrows make the original data immutable for their duration.
    3) Only allowed to pass one borrow at a time for write access/mutability.
*/

/* Rust Stack | Copy Types
    bool
    character
    numbers
    slices
    fix sized arrays
    tuples containing primitives
    function pointers
*/

fn main() {
    let mut s = String::from("A String");

    let mut a = &s[1..3];
    let b = &s[2..5];

    let c = &mut a;
    let d = &b;

    let y = false;
    let x = 10;

    own_and_borrow_stuff(&mut s, &y, x);
}

fn own_and_borrow_stuff(a: &mut String, b: &bool, c: u8) {}

// fn main() {
//     let mut a = String::from("A String");
//     {
//         let b = &a;
//         let c = &a;
//         let d = &a;

//         println!("{}, {}, {}", b, c, d);
//     }
//     a.pop();
//     {
//         let x = &mut a;
//         x.pop();
//     }
//     println!("{}", a);
// }

/*
    main
    ptr: a -> ptr: 0
              len: 8
              cap: 8
         b -> ptr: 15
              len: 8
              cap: 8
*/

/*
    | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
    |"A"|" "|"S"|"t"|"r"|"i"|"n"|"g"|
*/

/*
    global frame
    ptr main -> main()
*/
// fn main() {
//     let x = 10;
//     let y = x;
//     let z = x;

//     copy(true);
//     copy("a");
//     copy("a slice");
//     copy(x);
//     copy(String::from("Test"));
// }

// fn copy<T>(t: T) -> T
// where
//     T: Copy,
// {
//     let x = t;
//     x
// }

/*
    main
    x = 10
    y = 10
    z = 10
*/

/*
    main
    ptr: a -> ptr: 0
              len: 8
              cap: 8
    a.pop()
         a -> ptr: 0
              len: 7
              cap: 8
*/

/*
    | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
    |"A"|" "|"S"|"t"|"r"|"i"|"n"|"g"|
*/

// fn main() {
//     let x = 10;

//     let y = x;

//     print(x, y);
// }

// fn print(x: u8, y: u8) {
//     println!("{}, {}", y, x);
// }

/*
    ptr: main
    args:
    local vars: x = 1
        print(x)
    return value: ()
    drop x
*/

/*
    print
        a = 1 as u32
        println!("value {}", a)
    return value ()
    drop a
*/

/*
    println!()
    -> ()
*/

编译器针对一个代码分析:

fn main() {
    let x = &42;
}

Shell下输入如下命令:

$ rustc --edition 2018 --emit mir ex5.rs

可以得到如下mir中间代码:

// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
fn  main() -> () {
    let mut _0: ();                      // return place in scope 0 at ex5.rs:1:11: 1:11
    let _1: &i32;                        // in scope 0 at ex5.rs:2:9: 2:10
    let mut _2: &i32;                    // in scope 0 at ex5.rs:2:13: 2:16
    scope 1 {
        debug x => _1;                   // in scope 1 at ex5.rs:2:9: 2:10
    }

    bb0: {
        StorageLive(_1);                 // bb0[0]: scope 0 at ex5.rs:2:9: 2:10
        _2 = const main::promoted[0];    // bb0[1]: scope 0 at ex5.rs:2:13: 2:16
                                         // ty::Const
                                         // + ty: &i32
                                         // + val: Unevaluated(DefId(0:3 ~ ex5[317d]::main[0]), [], Some(promoted[0]))
                                         // mir::Constant
                                         // + span: ex5.rs:2:13: 2:16
                                         // + literal: Const { ty: &i32, val: Unevaluated(DefId(0:3 ~ ex5[317d]::main[0]), [], Some(promoted[0])) }
        _1 = _2;                         // bb0[2]: scope 0 at ex5.rs:2:13: 2:16
        StorageDead(_1);                 // bb0[3]: scope 0 at ex5.rs:3:1: 3:2
        return;                          // bb0[4]: scope 0 at ex5.rs:3:2: 3:2
    }
}

promoted[0] in  main: &i32 = {
    let mut _0: &i32;                    // return place in scope 0 at ex5.rs:2:13: 2:16
    let mut _1: i32;                     // in scope 0 at ex5.rs:2:14: 2:16
    scope 1 {
    }

    bb0: {
        _1 = const 42i32;                // bb0[0]: scope 0 at ex5.rs:2:14: 2:16
                                         // ty::Const
                                         // + ty: i32
                                         // + val: Value(Scalar(0x0000002a))
                                         // mir::Constant
                                         // + span: ex5.rs:2:14: 2:16
                                         // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) }
        _0 = &_1;                        // bb0[1]: scope 0 at ex5.rs:2:13: 2:16
        return;                          // bb0[2]: scope 0 at ex5.rs:2:13: 2:16
    }
}

看下你写下main.rs这个代码:

let x = &42;

转化成 mir 代码部分:

let mut _0: &i32;  //  分配一个 <_0: LV> 类型是 <&i32>
let mut _1: i32;   //  分配一个 <_1: RV> 类型是 <i32>
_1 = const 42i32;  //  把 <const 42i32> 写入 <_1: RV> 中
_0 = &_1;          //  把 <_1: RV> 的地址写入 <_0: LV> 中

update-alternative

bantana
30 March 2020

update

update-alternative --install [alias] [name] [file] [priority]

example:

$ sudo update-alternative --install /usr/bin/gcc gcc /usr/bin/gcc-10 20

check:

$ gcc --version

gcc (Ubuntu 10-20200324-1ubuntu1) 10.0.1 20200324 (experimental) [master revision 596c90d3559:023579257f5:906b3eb9df6c577d3f6e9c3ea5c9d7e4d1e90536] Copyright (C) 2020 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

clang-10

$ sudo update-alternative --config cc


There are 2 choices for the alternative cc (providing /usr/bin/cc).

  Selection    Path            Priority   Status
------------------------------------------------------------
  0            /usr/bin/gcc     20        auto mode
* 1            /usr/bin/clang   10        manual mode
  2            /usr/bin/gcc     20        manual mode

Press <enter> to keep the current choice[*], or type selection number: 

$ sudo update-alternative --config c++


There are 2 choices for the alternative c++ (providing /usr/bin/c++).

  Selection    Path              Priority   Status
------------------------------------------------------------
  0            /usr/bin/g++       20        auto mode
* 1            /usr/bin/clang++   10        manual mode
  2            /usr/bin/g++       20        manual mode

Press <enter> to keep the current choice[*], or type selection number:

passing arguments from Makefile

bantana
23 March 2020

Makefile:

run: all
    $(BUILD_DIR)/$(TARGET) $(ARGS)

Command:

make run ARGS='arg1 arg2 arg3'

equal:

$(BUILD_DIR)/$(TARGET) arg1 arg2 arg3

Multipass

bantana
28 August 2019

introduction

What is multipass? Multipass is a lightweight VM manager for Linux, Windows and macOS. It's designed for developers who want a fresh Ubuntu environment with a single command. It uses KVM on Linux, Hyper-V on Windows and HyperKit on macOS to run the VM with minimal overhead. It can also use VirtualBox on Windows and macOS. Multipass will fetch images for you and keep them up to date. Since it supports metadata for cloud-init, you can simulate a small cloud deployment on your laptop or workstation.

Install

$ brew cask install multipass

Basic Usage

Help command

$ multipass launch --help

Usage: multipass launch [options] [[<remote:>]<image> | <url>]
Create and start a new instance.

Options:
  -h, --help           Display this help
  -v, --verbose        Increase logging verbosity, repeat up to three times for
                       more detail
  -c, --cpus <cpus>    Number of CPUs to allocate
  -d, --disk <disk>    Disk space to allocate. Positive integers, in bytes, or
                       with K, M, G suffix. Minimum: 512M.
  -m, --mem <mem>      Amount of memory to allocate. Positive integers, in
                       bytes, or with K, M, G suffix. Mimimum: 128M.
  -n, --name <name>    Name for the instance
  --cloud-init <file>  Path to a user-data cloud-init configuration, or '-' for
                       stdin

Arguments:
  image                Optional image to launch. If omitted, then the default
                       Ubuntu LTS will be used.
                       <remote> can be either ‘release’ or ‘daily‘. If <remote>
                       is omitted, ‘release’ will be used.
                       <image> can be a partial image hash or an Ubuntu release
                       version, codename or alias.
                       <url> is a custom image URL that is in http://, https://,
                       or file:// format.

Find available images

$ multipass find

Image                   Aliases           Version          Description
snapcraft:core          core16            20190819         Snapcraft builder for Core 16
snapcraft:core18                          20190820         Snapcraft builder for Core 18
16.04                   xenial            20190814         Ubuntu 16.04 LTS
18.04                   bionic,lts        20190813.1       Ubuntu 18.04 LTS

Launch a fresh instance of the current Ubuntu LTS

you can simple use $ multipass launch bionic , the instance vm while use the default value

my example create use -n (name) -d (disk usage) -m (amount of memory):

$ multipass launch -n myubuntu -d 4G -m 1G bionic

Downloading Ubuntu 18.04 LTS..........
Launched: myubuntu

Check out the running instances

$ multipass list

Name                    State             IPv4             Image
myubuntu                Running           192.168.64.5     Ubuntu 18.04 LTS

Learn more about the VM instance you just launched

$ multipass info myubuntu

Name:           myubuntu
State:          Running
IPv4:           192.168.64.5
Release:        Ubuntu 18.04.3 LTS
Image hash:     babd5399b947 (Ubuntu 18.04 LTS)
Load:           0.00 0.03 0.00
Disk usage:     990.4M out of 3.7G
Memory usage:   73.1M out of 986.0M

Connect to a running instance

$ multipass shell myubuntu
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-58-generic x86_64)

...

Don't forget to logout (or Ctrl-D) or you may find yourself heading all the way down the Inception levels... ;)

Run commands inside an instance from outside

$ multipass exec myubuntu -- lsb_release -a

No LSB modules are available.
Distributor ID:    Ubuntu
Description:    Ubuntu 18.04.3 LTS
Release:    18.04
Codename:    bionic

Stop an instance to save resources

$ multipass stop myubuntu

Name                    State             IPv4             Image
myubuntu                Stopped           --               Ubuntu 18.04 LTS

Delete the instance

$ multipass delete myubuntu

It will now show up as deleted:

Name                    State             IPv4             Image
myubuntu                Deleted           --               Not Available

And when you want to completely get rid of it:

$ multipass purge

$ multipass list
No instances found.

Get help

multipass help
multipass help <command>

Get involved!

Here's a set of steps to build and run your own build of multipass:

Build Dependencies

cd <multipass>
apt install devscripts equivs
mk-build-deps -s sudo -i

Building

cd <multipass>
git submodule update --init --recursive
mkdir build
cd build
cmake ../
make

Running multipass daemon and client

sudo <multipass>/build/bin/multipassd &
<multipass>/build/bin/multipass launch --name foo

dnsmasq and dnscrypt-proxy

bantana and
10 July 2019

Introduct

dns resolv --> dnsmasq --> dnscrypt-proxy --> internet dnscrypt

Install

If en0 is internet link:

sudo tcpdump -i en0 -vvv 'port 443'

Install dnscrypt-proxy:

brew install dnscrypt-proxy

sudo vi /usr/local/etc/dnscrypt-proxy.toml

>> modify:

  listen_addresses = ['127.0.0.1:5300', '[::1]:5300']

sudo brew services restart dnscrypt-proxy

Install dnsmasq:

brew install dnsmasq

sudo vi /usr/local/etc/dnsmasq.conf

>> modify:

  server=127.0.0.1#5300

sudo brew services restart dnsmasq

Change local dns resolv with 127.0.0.1.

Debug

$ dig +dnssec icann.org

  ; <<>> DiG 9.10.6 <<>> +dnssec icann.org
  ;; global options: +cmd
  ;; Got answer:
  ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50952
  ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

  ;; OPT PSEUDOSECTION:
  ; EDNS: version: 0, flags: do; udp: 1472
  ;; QUESTION SECTION:
  ;icann.org.            IN    A

  ;; ANSWER SECTION:
  icann.org.        3554    IN    A    192.0.43.7
  icann.org.        3554    IN    RRSIG    A 7 2 600 20190719002550 20190627174048 61202 icann.org. YQzj2jgkjzjX+LqU7eajQxD4hnACTSX3JtrZOpbEzUoUG2BlJ13CcTKs Q1JPaEo6AR5U22J2tEyHzrnv0bF5Wj8erdtRjmIKMTVuWNOYDI76iBWZ Vm2DT5WlXSypkqXz3bdkr5I0gb6bvnICVzCOejS/QIQiO4c6f6qJcaT2 U0U=

  ;; Query time: 0 msec
  ;; SERVER: 127.0.0.1#53(127.0.0.1)
  ;; WHEN: Wed Jul 10 18:20:14 CST 2019
  ;; MSG SIZE  rcvd: 223

See the index for more articles.