cargo 使用本地库

前天又看了下Rust,乘热打铁,动手写点东西,写什么好呢?
这一年我遇到过最多的就是读配置了,从写了好几个东西都是需要从文件读参数或者写参数,甚至是原位替换! 从最开始用C写一点scalability都没有的到手写Hash再到接触C++后用容器,以及其间也用Ruby写过来应对需求。
所以打算写个库来自己用(我喜欢造轮子:blush:),在此之前我先用Rust把sudodev写一遍熟悉下先。

cargo new sdev --bin 新建一个项目,开始动手写。当写到读fstab的时候,发现Rust没有C++里面的std::getline,于是就自己实现一个,那么,问题就是我的Cargo.toml里面的依赖是这样啊

[dependencies]
nix = "*"

连路径都没写,直接从GitHub下载的。。去https://doc.crates.io 找文档,在这儿的页面末尾看到

It’s gotten to the point that we probably want to split out a separate crate for others to use. To do this Cargo supports path dependencies which are typically sub-crates that live within one repository.

一个crate里面可以有许多个子crates,我现在大概就是这种情况。于是就试了。
这个项目的目录结构是这样的

tree . 
.
├── Cargo.lock
├── Cargo.toml
└── src
    ├── main.rs
    ├── not
    │   ├── Cargo.toml
    │   └── not.rs
    └── readline
        ├── Cargo.lock
        ├── Cargo.toml
        └── readline.rs

一个crate里面包含了两个crate,这两个子crate都是用cargo new name创建,这两个子crate的Cargo.toml内容如下:

not/Cargo.toml

[lib]
name = "not"
path = "not.rs"

readline/Cargo.toml

[lib]
name = "readline"
path = "readline.rs"

其中lib告诉cargo这是要编译成一个库,name是指这个这个crate的名字path是这个库的路径(file path)。在引入这个库时需要用extern关键字来指明,我需要使用这两个库,那么我需要在src/main.rs里面增加

extern crate readline;
extern crate not;

使用这两个库中的东西,还需要

use readline::readline::Readlines;  // crate::mod::trait;
use not::not::BitNot;               // crate::mod::trait;

现在,库写好了,怎样让Cargo编译的时候找到这些库呢?Cargo的文档是这样写的

[dependencies]
hello_utils = { path = "hello_utils" }

它这里的目录结构大概是

hello_world
|
|___Cargo.toml
|___ main.rs
|___hello_utils
    |
    |___ Cargo.toml
    |___ lib.rs      

在项目的顶层目录中的Cargo.toml中加入依赖。

按照这种写法,我顶层目录中的Cargo.toml是这样

[dependencies]
nix = "*"

readline = { path = "src/readline" }

not = { path = "src/not" }

配置中的section字段不允许有重复,另外上面的"src/"在这儿是必需的!去掉的话会出现”Could not find Cargo.toml in xxx/xxx“。

另外,还有一种写法(Stack Overflow上有人的回答),估计也是toml的语法。

[dependencies]
nix = "*"

[dependencies.readline]
path = "src/readline"

[dependencies.not]
path = "src/not"

附上第一次在实践中写的有用的mod:

not.rs

pub mod not {

    trait BitAnd<T = Self> {
        type Output;
        fn bitand(&self, other: &T) -> T;
    }

    impl BitAnd for Vec<String> {
        type Output = Vec<String>;

        fn bitand(&self, other: &Vec<String>) -> Vec<String> {
            let mut res: Vec<String> = Vec::new();

            for i in self.iter() {
                if other.contains(i) {
                    res.push(i.clone());
                }
            }

            res
        }
    }

    // actually: (a | b)~(a & b)
    pub trait BitNot<T = Self> {
        type Output;
        fn bitnot(&self, other: &T) -> T;
    }

    impl BitNot for Vec<String> {
        type Output = Vec<String>;

        fn bitnot(&self, other: &Vec<String>) -> Vec<String> {
            let mut res: Vec<String> = Vec::new();
            let same: Vec<String> = self.bitand(other);

            for i in self.iter() {
                if !same.contains(i) {
                    res.push(i.clone());
                }
            }

            for j in other.iter() {
                if !same.contains(j) {
                    res.push(j.clone());
                }
            }

            res.sort();
            res
        }
    }
}

readline.rs

pub mod readline {

    use std::fs::File;
    use std::io::Read;

    pub trait Readlines {
        fn readlines(mut self) -> Vec<String>;
    }

    impl Readlines for File {
        fn readlines(mut self) -> Vec<String> {
            let mut content = String::new();
            match self.read_to_string(&mut content) {
                Ok(_) => {},
                Err(e) => panic!(e)
            }
            let res = content.lines()
                .map(move |x: &str| x.to_string())
                .collect();

            res
        }
    }
}