Rust语言中的单位类型unit type是什么

每个学习Rust语言的读者碰到单位类型unit type ()的时候都非常疑惑这究竟是个什么鬼?
本文总结了stackoverflow上面的贴子 What is the purpose of the unit type in Rust?加上我自己的理解。

  1. unit type是一个类型,有且仅有一个值,都写成小括号()
  2. 单元类型()类似c/c++/java语言中的void。当一个函数并不需要返回值的时候,c/c++/java中函数返回void,rust则返回()。但语法层面上,void仅仅只是一个类型,该类型没有任何值;而单位类型()既是一个类型,同时又是该类型的值。
  3. 单元类型()也类似c/c++/java中的null,但却有很大不同。 null是一个特殊值,可以赋给不可类型的值,例如java中的对象,c中指向struct实例的指针,c++中的对象指针。但在rust中,()不可以赋值给除单元类型外的其它的类型的变量,()只能赋值给()。
  4. Rust标准库中使用单元类型()的一个例子是HashSet。一个HashSet只不过是HashMap的一个非常简单地包裹,写作:HashMap<T, ()>。HashMap的第二个泛型类型参数即用了单元类型()
  5. 可以用Result<(), MyErrorType>代替Option,某些开发者认为Result<(), MyErrorType>语义上能更简明地表示一个“结果”。

You could argue that Option is ‘better’, but I disagree; the Result’s naming conventions mean it’s explicitly clear what’s going on.

使用Prettier,Husky和lint-staged,在提交javascript代码前自动格式化代码

目标

为防止提交不合格的代码排版到仓库中,我们希望在提交前格式化代码,使之符合项目的代码格式规范,而且尽可能保证逻辑正确。即目标有两点:
1. 对git staged的代码强行格式化,使符合规范。对于没有被staged的代码或文件,将完全忽略。
2. 跑单元测试。只有通过了单元测试才能提交代码。

安装依赖

$ npm install --save-dev husky lint-staged

先安装husky和lint-staged。注间在安装husky之前需要确保工程目录已经是一个合法的git仓库。因为,安装husky时需要给git仓库安装预提交钩子(precommit hook)。
Lint-staged提供了一个关键的功能,它能找到所有被git staged的文件,而不是工程下面所有的文件,然后通过管道交给Prettier做格式化。

修改npm脚本

对npm script做如下改动:

  "scripts": {
    "precommit": "lint-staged"
  },
  "lint-staged": {
    "*.{js,jsx,json}": ["prettier --write", "git add"]
  }

scripts.precommit由Husky处理,它会依照配置在把代码正式提交到仓库前运行lint-staged(当然我们也可以配置其他命令)。
Lint-staged再找到属于它自己的配置部分,即lint-staged键下面的配置。
在此例中,所有staged状态的,且扩展名是.js, .jsx, 和.json文件都会自动交由Prettier做格式化,然后再提交。

为何一定要用lint-staged

You might wonder why we don’t just use Husky to run a few npm scripts.
你可以会疑惑,为什么一定要罗里罗嗦地用lint-staged,用Husky直接调用npm脚本不更简单么。
这是因为我们用lint-staged拿到staged状态的文件,而不是所有的文件。
你的node工程文件中可能已经有如下做代码格式化的脚本

"format": "prettier --write '**/*.{js,jsx}'"

你可能会因此如下配置

"lint-staged": {
  "*.{js,jsx,json}": ["npm run format", "git add"]
}

如果是这样配置的话,就没有必要用lint-staged了。因为,这样的配置会把prettier应用到所有匹配.js,.jsx.json的文件上,而不是仅staged状态的文件。

把跑单元测试加入预提交检查

Husky还能够帮你把跑单元测试加入到预提交检查中,保证只有通过了单元测试才正式提交代码到仓库。
如下是一个典型的配置:

"scripts": {
    "test": "exit 0",
    "precommit": "lint-staged && npm test"
  },
  "lint-staged": {
    "*.{js,jsx,json}": ["prettier --write", "git add"]
  }

实际项目中要修改scripts.test部分,视工程使用的单元测试框架Jest, 或Mocha ,本示例简单的返回0,表示无条件通过。

python用multiprocessing起子进程,父进程被杀掉时,如何让子进程也自动退出

最近用python3写了一段代码,代码会起一个子进程做一些辅助的事情。但发现父进程被杀死后,这个子进程变成了孤儿进程,仍然在运行。
起进程的代码大概如下:

from multiprocessing import Process, Queue    
__q = Queue() 
__p = Process(target=__run, args=(__q,))                                                                                                           
__p.start()   

很不方便,那怎样让父进程退出后,子进程也跟着退出呢。
一番google之后,原来可以用一行代码解决:

from multiprocessing import Process, Queue    
__q = Queue() 
__p = Process(target=__run, args=(__q,))   
__p.daemon = True    # 加上这一行代码
__p.start()   

设置子进程的daemon属性为True。这样,当在控制台上用ctrl-c,退发送SIGTERM杀掉父进程后,子进程也会跟着退出,不会成为孤儿进程。
参考python官方的解释:

daemon
The process’s daemon flag, a Boolean value. This must be set before start() is called.
The initial value is inherited from the creating process.
When a process exits, it attempts to terminate all of its daemonic child processes.

官方解释的重点是daemon flag必须在start()前被设置。
还有一个坑人的地方,貌似父进程必须被SIGTERM信号杀死,或者自行自然退出,子进程才会退出,否则还是变成了孤儿进程。在stackoverflow上有人提到了这点:
https://stackoverflow.com/questions/25542110/kill-child-process-if-parent-is-killed-in-python

I want to mention, that if parent process was killed using SIGKILL (kill -9) then daemon processes won’t stop.

我想,这或许应该是由于-9(SIGKILL)会立刻中止父进程,于是父进程没有机会通知子进程,比如用atexit设置的进程退出时调用的hook函数就没机会执行了。

Rust中的&str和String有什么区别

&str和String让Rust初学者困惑不已。这里简单的介绍一个&str和String的区别。

String就像是Vec一样是动态分配在堆heap上的字符串类型,它指向一串UTF8编码的字节序。当需要拥有该字符串,要更改字符串的内容,要把字符串在线程之间传递,要在运行时动态创建字符串时使用此类型。

str也指向一串UTF8编码的字节序,但并不拥有指向的字符串,而仅仅只是一个“string slice”。slice的详细解释点链接。str绝大多数情况下以引用&str的形式出现。又被称为字符串字面值(string literal)。
和String指向的字符串肯定分配在堆上面不一样,str指向的字符串可能出现在各种地方:
1. 在程序的全局静态空间(.text段??)。一个string literal的类型和生命期是:&’static str。字符串的数据硬编码进生成的程序或库文件,但程序加运行时,字符串加载进内存。
2. 分配在堆上面 String有一个方法是as_str,它返回一个&str,指向String管理的堆上面的字符串。另外,String有实现Deref<Target=str>这样一个Trait,让所有接受&str作为参数的函数,都可以传String的引用。编译器会自动把&String转成&str。
3. 分配在栈上面 如下面的代码,变量stack_str的类型是&str,它指向栈上面的字符串。

use std::str;

let x: &[u8] = &[b'a', b'b', b'c'];
let stack_str: &str = str::from_utf8(x).unwrap();

如果以C++程序员的视角看String和&str。一个Rust的String类似std::string,完全拥有并管理(创建,销毁)需要的内存。而Rust的&str则类似char*(当然实际上要复杂很多),仅仅指向字符串,不负责管理对应的内存。

再看看String和&str的互转化:
&str转String可以用三种方法

    let a: String = "hello rust".into();
    let c: String = "hello rust".to_string();
    let b: String = String::from("hello rust");
    println!("{}", a);   // 都输出 hello rust
    println!("{}", b);
    println!("{}", c);

String转&str可以直接引用String类型的值或者调用String的as_str方法。

let a: String = String::from("hello rust");
let aaa: &str = &a;
let bbb: &str = a.as_str();

转载请注明出处链接 http://blog.ykyi.net/2019/10/rust%E4%B8%AD%E7%9A%84str%E5%92%8Cstring%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB/

linux怎么把一个正在前台运行的进程用nohup放入后台运行

假设进程在控制台运行。
三个步骤:
1. ctrl + z 停止正在运行的进程
2. $ bg 命令把该进程放入后台
3. disown -h [job的序号] 后台的进程有类似%1, %2, %3的序号。

至此,一个正在控制台前台运行进程被放入后台,且忽略SIGHUP信号。这样,即使关闭了控制台,也不会杀死这个进程了。

python自制包并用pip免提交到pypi仅安装到本机

不得不说python的自制包的相关工具真是多且混乱,什么setuptools,什么distutils,什么wheel,什么egg!!怎么有这么多啊??

而且我的需求且且是创建一个自制包管理自己常用的代码,也必不想提交到PyPI,仅仅只需要安装到本机就行。

下面就是几个关键步骤。

  1. 文件目录布局
    ├── package1
    │ └──-├── init.py
    | |── mod1.py
    │ └── mod2.py
    ├── setup.py
    |── README.md
  2. 编写setup.py文件,类似如下:
from setuptools import setup, find_packages

setup(
    name="kamustools",
    version="1.0.1",
    author="Kamuszhou",
    author_email="zausiu@gmail.com",
    description="tools used by kamuszhou exclusively.",
    license="BSD",
    keywords="kamus",
    url="https://blog.ykyi.net",
    packages=find_packages(),
    long_description="Long descrition is actually short...",
    classifiers=[
        "Development Status :: 3 - Alpha", 
        "Topic :: Utilities",
        "License :: OSI Approved :: BSD License",
    ],
)
  1. python3 setup.py sdist bdist_wheel
  2. pip install ./dist/kamustools-1.0.1.tar.gz

就这四步,其他各种功能,以后要用了再慢慢看吧,实在太庞杂了。满足我自己需求的这四个简单步骤就记录在这里。