Rust 语言入门

Rust

安装

Hello world

  1. 新建一个文件夹

  2. 创建main.rs文件

  3. 编写代码

    fn main(){
        println!("hello, world!");
    }
    
  4. 编译运行

    rustc main.rs
    

cargo

cargo是一个依赖管理和编译集成工具。

cargo --version

cargo 创建工程

cargo new hello_cargo

cargo.toml是依赖管理文件

编译

cargo build

运行

.\target\debug\hello_cargo.exe
or 
cargo run

发布

cargo build --release

Guessing Game

  1. 创建新项目

    cargo new gassing_game
    
  2. 运行一下

    cargo run
    
  3. 输入输出

    //使用标准库,不需要外部依赖的库
    use std::io;
    fn main() {
        println!("Hello, world!");
        println!("Please input a number!");
    	//定义一个字符串变量
        let mut guess = String::new();
        //获取标准输入
        io::stdin().read_line(&mut guess).expect("Failed to read line");
    	//打印变量
        println!("your input is {}",guess);
    }
    
    
  4. 导入随机数库

    //cargo.toml
    
    [dependencies]
    rand = "0.8.3"
    
  5. 更新镜像源

    在user/.cargo/config
    [source.crates-io]
    registry = "https://github.com/rust-lang/crates.io-index"
    replace-with = 'ustc'
    [source.ustc]
    registry = "git://mirrors.ustc.edu.cn/crates.io-index"
    
  6. 编译,下载依赖

    cargo build
    
  7. 使用随机数

    
    use rand::Rng;
    fn main() {
        let secret_number = rand::thread_rng().gen_range(1..101);
        println!("secret number is {}",secret_number);
    }
    
  8. 比较

    use std::io;
    use rand::Rng;
    use std::cmp::Ordering;
    fn main() {
        println!("Hello, world!");
        let secret_number = rand::thread_rng().gen_range(1..101);
        println!("secret number is {}",secret_number);
        println!("Please input a number!");
        let mut guess = String::new();
        io::stdin().read_line(&mut guess).expect("Failed to read line");
        let guess:u32 = guess.trim().parse().expect("please input a number!");
        println!("You guessed {}",guess);
        match guess.cmp(&secret_number) {
            Ordering::Less => println!("too small!"),
            Ordering::Greater => println!("too big!"),
            Ordering::Equal => println!("You win!"),
        }
    
    }
    
    
  9. 循环

    use std::io;
    use rand::Rng;
    use std::cmp::Ordering;
    fn main() {
        println!("Hello, world!");
        let secret_number = rand::thread_rng().gen_range(1..101);
        // println!("secret number is {}",secret_number);a
        loop {
            println!("Please input a number:");
            let mut guess = String::new();
            io::stdin().read_line(&mut guess).expect("Failed to read line");
            let guess:u32 = guess.trim().parse().expect("please input a number!");
            println!("You guessed {}",guess);
            match guess.cmp(&secret_number) {
                Ordering::Less => println!("too small!"),
                Ordering::Greater => println!("too big!"),
                Ordering::Equal => {
                    println!("You win!");
                    break;
                }
            }
        }
    }
    
    

变量

  1. 创建变量项目

    cargo new variables
    
  2. 未加mut的变量不可修改值

    fn main() {
        println!("Hello, world!");
        let mut x = 6;
        println!("x is {}",x);
        x=7;
        println!("x is {}",x);
    }
    
    
  3. 常量

        const MAX_VALUE:i32 = 100;
        println!("max value is {}",MAX_VALUE);
    
  4. shadow

        let y = 4;
        let y = y +3;
        println!("y is {}",y);//7
        {
            let y = y +2;
            println!("y is {}",y);//9
        }
        println!("y is {}",y);//7
    
        let str = "hello world";
        let str = str.len();
        println!("str's len is {}",str);
    

Data Types

 let x = "46".parse().expect("not a number!");//illegal
 let x:i32 = "46".parse().expect("not a number!");

integer

image-20220129120123860

float

默认64位

bool

char

可以有表情

let em = '🆒';

Tuple

let tup = (1,2.3,'h');
let (x,y,z) = tup;
println!("x:{},y:{},z:{}",tup.0,tup.1,tup.2);

Array

let arr = [1,2,3,4,5];
let arr:[i32;5] = [1,2,3,4,5];
let arr = [0;100];//100 个 0

Never

一种数据类型,使用!表示,用于永远不可能有返回值的函数;

#![feature(never_type)]
fn foo()->i32 {
    let x: ! = {
        return 123
    };
}

动态类型

零类型

大小为零ZST,空

底类型

Never的实现,无

Function

fn add(a:i32,b:i32)->i32{
    let a = {
        let a = 1;
        a+1
    };
    a
}

control flow

if

类型一致;

condition只能是bool值

多于2个else if 使用match代替

fn main() {
    println!("Hello, world!");
    let condition = true;
    if condition{
        println!("condition is true!");
    }else {
        println!("condition is false!");
    }
}

let x = if condition { 6 } else {"six"};//类型不一样,编译错误
let x = if condition { 6 } else {7};

loop

loop前面可以有标号

break只跳出一层、

fn test_loop(){
    let mut count = 0;

    'flag: loop {
        println!("count is {}",count);
        let mut remainder = 10;
        loop {
            println!("remainder = {}",remainder);
            if remainder == 9 {
                break;
            }
            if count ==2 {
                break 'flag;
            }
            remainder -= 1; 
        }
        count+=1;
    }
    println!("end count is {}",count);
}

while

while true不行,控制流分析。

fn while_true(x:i32)->i32{
    while true {
        return x+1;
    }
    x//resolve
}
fn test_while(){
    let mut iter = 3;
    while iter > 0 {
        println!("iter is {}",iter);
        iter -= 1;
    }
}

for

本质是一个迭代器。

fn test_for(){
    let  a = [0;3];
    for e in a {
        println!("{}",e);
    } 
 
    for e in (1..4).rev() {
        println!("{}",e);
    }
}

ownership

拥有权是针对堆区的数据来说的。

对于基本数据类型都是存在栈上,所以无关拥有权

fn main() {
    println!("Hello, world!");
    let x = 4;
    let y =x;
    println!("{}{}",x,y);
    let s = "hello";
    let s1 = s;
    let flag = true;
    let flag1 = flag;
}

对于堆上的数据,赋值、传参都会移交拥有权,对于这种操作叫做move, 。

fn string_onwership(){
    let s = String::from("hello");
    let s1 = s;
    {
        let s2 = s1;
    }//s1,s2都不可用了
    let s3 = gives_onwership();//s3借用了hello的拥有权
    get_onwership(s3);//s3move给了函数
    //s3不可用了
}
fn gives_onwership()->String{
    let s = String::from("hello");
    s
}
fn get_onwership(s:String){
    let s1 = s;
}

引用

不移交拥有权

fn main() {
    println!("Hello, world!");
    let s = String::from("hello");
    println!("{}",get_len(&s)); 
    println!("{}",s);
}

fn get_len(s:&String)->usize{
    s.len()
}
fn main() {
    println!("Hello, world!");
    let s = String::from("hello");
    println!("{}",get_len(&s)); 
    println!("{}",s);
    let mut s1 = String::from("hello");
    change(&mut s1);
    println!("{}",s1);
}

fn change(s:&mut String){
    s.push_str("hello");
}

可变引用只能有一次。

不可变引用可以有无限次。


fn quote(){
    let mut s = String::from("hello");
    let s1 = &mut s;
    let s2 = &mut s;//错误
    println!("{}{}",s2,s1);
}
fn quote(){
    let mut s = String::from("hello");
    let s1 = &mut s;
    let s2 = &s1;
    println!("{}{}",s2,s1);
}

可变变量不能同时存在可变引用和不可变引用,可以有多个不可变引用。

fn quote1(){
    let mut my_str = String::from("A mutable string");
    let first_unmutable_reference = &my_str;
    let second_unmutable_reference = &my_str;
     println!("{} {}", first_unmutable_reference, second_unmutable_reference);
}

可变变量不能同时存在多个可变引用。

可变引用移交所有权。

引用的传递叫借用。

切片

fn slice(){
    let s = String::from("hello world");
    let s1 = &s[1..2];
}

Struct

三种

  • 正常具名结构体

  • 元组

    • NewType

      一个元组结构体只有一个字段的时候,我们称之为New Type 模式
      struct Interger(i32);
      type Int = i32;
      
    • 也可以使用type 关键字为一个类型创建别名

  • 单元结构体:没有任何子段的结构体

define

struct Student {
    email:char,
    name:char,
    age:f64,
}
struct Color(i16,u32,String);

initial

let mut a = Student{
    email:String::from("123@163.com"),
    name:String::from("gre"),
    age:23.1,
};
let mut b = Student{
        name:String::from("hello"),
        ..a
};//此方法会move

访问

println!("{}",a.age);
println!("{}",c.1);

方法

struct Rectangle{
    width:u32,
    height:u32,
}

impl Rectangle {
    fn area(&self)->u32{
        self.width * self.height
    }
}
let rect = Rectangle{
    width:10,
    height:10,
};
println!("{}", rect.area());
let squ = Rectangle::square(4);
println!("{}",squ.area());

Enums

enum IpAddr {
    V4(String),
    V6,
}
fn main() {
    let four = IpAddr::V4(String::from("127.0.0.1"));
    // println!("{}",four);
}

option

Rust没有null

提供了Option,预导入了,可以直接使用。

pub enum Option<T> {
    /// No value
    #[lang = "None"]
    #[stable(feature = "rust1", since = "1.0.0")]
    None,
    /// Some value `T`
    #[lang = "Some"]
    #[stable(feature = "rust1", since = "1.0.0")]
    Some(#[stable(feature = "rust1", since = "1.0.0")] T),
}
    let a:Option<i32> = Some(12);
    let b:i32 = 2;
    let c = a+b;//error 类型不同

match

if let

while let

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}
fn value_in_cents(coin:Coin)->u8{
    match coin {
        Coin::Penny=>1,
        Coin::Nickel=>5,
        Coin::Dime=>10,
        Coin::Quarter=>{
            20+5
        }
    }
}
fn plus_one(x:Option<i32>)->Option<i32>{
    match x {
        None => None,
        Some(i)=>Some(i+1),
    }
}
fn main() {
    println!("Hello, world!");
    println!("{}",value_in_cents(Coin::Nickel)); 
    plus_one(Some(3));
}

let v = 0u8;
match v {
    1 => println!("{}",1),
    _ => (),
}
let v = Some(0u8);
if let Some(3) = v {
    println!("three");
}

指针

  • 原生指针
  • 引用
  • 函数指针
  • 智能指针

原生指针

原生指针不安全,需要使用unsafe,两种

*const T
*mut T
fn main() {
    let mut x = 10;
    let ptr_x = &mut x as *mut i32;
    let y = Box::new(20);
    let ptr_y = &*y as *const i32;
    unsafe{
        *ptr_x += *ptr_y;
    }
    println!("x: {}",x);
}

Box是在堆上创建内容。

智能指针

非Rust独有,借鉴于C++

Rust默认将变量分配到栈空间,使用Box可以分配到堆空间,并且可以自动析构。

Box,可以通过* 解引用获取T

智能指针不止Box

#[derive(Debug)]
 struct Point{
     x:i32,
     y:i32,
 }

fn main() {
    let p1 = Point{x:12,y:13};
    let origin = Box::new(p1);
    let p2 = *origin;
    println!("{:?}",p2);    
}

Package,Crate,Module

集合

  • 线性:向量,双端队列,链表
  • Key-Value:无序哈希表,有序哈希表
  • 集合类型:无序集合,有序集合
  • 优先队列:二叉堆(优先队列)

vector

  • 标准库提供
  • 可以存储多个值
  • 只能存储相同数据类型
  • 值在内存中连续存放

创建

fn main() {
    println!("Hello, world!");
    let v:Vec<i32> = Vec::new();
    let v = vec![1,2,3]; 
}

更新

要么读,要么写

let mut v = vec![1,2,3]; 
v.push(2);
v.push(3);
v.push(4);

获取

    let mut v = vec![1,2,3]; 
    v.push(2);
    v.push(3);
    v.push(4);
    let third:&i32 = &v[3];
    println!("The third element is {}",third);
    match v.get(3) {
        Some(third) => println!("The third element is {}",third),
        None => println!("None"),
    }
	let third:&i32 = &v[100];//panic
    println!("The third element is {}",third);
    match v.get(100) {//ok
        Some(third) => println!("The third element is {}",third),
        None => println!("None"),
    }

遍历

    let v = vec![1,2,3,4,5];
    for ele in &v {
        print!("{}",ele);
    }
    let mut v = v;
    for ele in &mut v{
        *ele += 5;
        println!("{}",ele);
    }

删除

  • 出作用域就会删除

方不同数据类型

enum SpreadsheetCell {
    Int(i32),
    Float(f64),
    Text(String),
}
fn example(){
    let row = vec![
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Float(3.2),
        SpreadsheetCell::Text(String::from("hello")),
    ];
}

String

  • Byte的集合
  • 一些方法
    • 将byte解析为文本
  • UTF-8
str

切片形式str

创建
fn create(){
    let mut  s = String::new();
    let data = "initial contents";
    let s = data.to_string();
    let s1 = String::from("initial contents");
    let utf_char = String::from("こんにちは、世界!");
    
}
更新

类比vector

使用+类似add方法

fn update(){
    let mut s = String :: from("foo");
    let str = String::from("bar");
    s.push_str(&str);
    s.push('l');
    println!("{}",str);
}
拼接
fn cat(){
    let s1 = String::from("hello");
    let s2 = String::from("world");
    let s3 = s1 + &s2;
    println!("{}",s3);
    let s4 = format!("{}-{}",s2,s3);
    println!("{}",s4);
}
访问

字符簇

fn read(){
    let s = String::from("hello world");
    let a = s[1];//error
}
fn read(){
    let s = String::from("hello world");
    // let a = s[1];
    let len = String::from("1234").len();//4
    let len = String::from("ᖃᐅᔨᒪᒻᒪᕆᑦᑐᓄᑦ").len();//33
    //Unicode 标量值
    println!("{}",len);
}
fn type_check(){
    let s = String::from("शुभाः दुनिया!");
    for b in s.bytes(){
        println!("{}",b);
    }
    for c in s.chars(){
        println!("{}",c);
    }
}
切割
fn slice(){
    let hello = String::from("शुभाः दुनिया!");
    let s = &hello[0..6];//4 panic, each char 3 bytes
    println!("{}",s);
}

hashmap

  • HashMap<K,V>
创建
  • 堆上
use std::collections::HashMap;

fn create(){
    let mut scores:HashMap<String,i32> = HashMap::new();
    let mut scores = HashMap::new();
    scores.insert(String::from("age"),23);
}

fn create2(){
    let teams = vec![String::from("blue"),String::from("yellow")];
    let initial_score = vec![10,50];
    let scores:HashMap<_,_> = teams.iter().zip(initial_score.iter()).collect();
}
所有权
  • 对于实现了copy trait的类型,值会被复制到HashMap中

  • 对于拥有所有权的值(String),值会移动(move),所有权归HashMap

    fn ownership(){
        let key = String::from("jack");
        let value = String::from("32");
        let mut person = HashMap::new();
        // person.insert(key, value);
        // println!("{}{}",key,value);//error 
        person.insert(&key, &value);
        println!("{}{}",key,value);//ok
    }
    
获取
fn get(){
    let mut scores = HashMap::new();
    scores.insert(String::from("blue"), 10);
    scores.insert(String::from("yellow"), 50);
    let team_name = String::from("blue");
    let score = scores.get(&team_name);
    match score {
        Some(s) => println!("score is {}",s),
        None => println!("team not exist"),
    }
}
遍历

fn traverse(){
    let mut scores = HashMap::new();
    scores.insert(String::from("blue"), 10);
    scores.insert(String::from("yellow"), 50);
    for (k,v) in &scores{
        println!("{}-{}",k,v);
    }
}
更新
  • HashMap大小可变
  • 每个k对应一个v
  • 更新
    • 存在,替换,保留,合并
    • 不存在,添加
fn update(){
    let mut scores = HashMap::new();
    scores.insert(String::from("blue"), 10);
    scores.insert(String::from("blue"), 25);
    println!("{:?}",scores);
    scores.entry(String::from("yellow")).or_insert(50);
    scores.entry(String::from("blue")).or_insert(50);
    println!("{:?}",scores);//{"blue": 25, "yellow": 50}
}

错误处理

  • 编译错误并处理
  • 错误分类
    • 可恢复,文件未找到
    • 不可恢复,超出index
  • Rust没有异常处理机制
    • 可恢复,Result
    • 不可恢复:panic!宏

panic!

当panic!宏执行时:

  • 程序打印错误信息
  • 展开unwind,清理调用栈Stack
  • 退出程序

可以选择终止调用栈:

  • 不清理,直接退出
  • 内存需要os进行清理

让程序文件更小,可以把展开改为终止:

cargo.toml的profile

  • panic = ‘abort’
[profile.release]
panic = 'abort'

错误信息

RUST_BACKTRACE=1
fn main() {
    // panic!("crash and burn");
    let v = vec![1,2,3];
    v[22];
    println!("Hello, world!");
}

不带–release

Result

pub enum Result<T, E> {
    Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
    Err(#[stable(feature = "rust1", since = "1.0.0")] E),
}
use  std::fs::File;
fn example(){
    let f = File::open("hello.txt");
    let f = match f {
        Ok(file) => file,
        Err(error) => {
            panic!("error opening file {:?}",error);
        }
    };
}
use std::io::ErrorKind;
fn example2(){
    let f = File::open("hello.txt");
    let f = match f {
        Ok(file) => file,
        Err(error) => match error.kind() {
            ErrorKind::NotFound => match File::create("hello.txt"){
                Ok(fc) => fc,
                Err(e) => panic!("error creating file {:?}",e),
            },
            other_error => panic!("Error opening the file {:?}",other_error),
            
        }
    };
}

闭包

fn closure(){
    let f = File::open("hello.txt").unwrap_or_else(|error|{
        if error.kind() == ErrorKind::NotFound{
            File::create("hello.txt").unwrap_or_else(|error|{
                panic!("Error create file:{:?}",error);
            })
        }else {
            panic!("Error opening file {:?}",error);
        }
    });
}
unwrap

match表达式的一个快捷方法;

  • 如果是ok就返回ok的结果
  • err返回panic!宏
fn example3(){
    let f = File::open("hello.txt").unwrap();
}
expect

可以指定错误信息

fn example4(){
    let f = File::open("hello.txt").expect(&String::from("无法打开文件"));
}

传播错误

let res = example5();
use std::io::Read;
use std::io;
fn example5() -> Result<String,io::Error>{
    let f = File::open("hello.txt");
    let mut  f = match f {
        Ok(f) =>f,
        Err(e) => return Err(e),
    };
    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_)=>Ok(s),
        Err(e)=>Err(e),
    }
}

?错误传播快捷方式

fn example6() -> Result<String,io::Error>{
    let mut f = File::open("hello.txt")?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

?与from函数,可以有错误转换。

优化

fn example7()-> Result<String,io::Error>{
    let mut s = String::new();
    File::open("hello.txt")?.read_to_string(&mut s)?;
    Ok(s)
}
什么时候用panic

原则

  • 在定义一个可能失败的函数是优先考虑Result
  • 否则panic

场景

  • 演示概念
  • 原型代码:unwrap、expect
  • 测试

一定是ok时可以使用unwrap

  • 传入无意义参数时
  • 调用外部不可控代码
  • 可预期的用Result
  • 值验证

泛型,Trait,生命周期

提取函数消除重复代码

泛型

提高复用能力

相对于模板

泛型函数
fn largest<T>(list:&[T])->T{
    let mut largest = list[0];
    for &item in list{
        if item > largest{ // todo 
            largest = item;
        }
    }
    largest
}
struct泛型
struct Point<T>{
    x:T,
    y:T,
}
fn main() {
    println!("Hello, world!");
    let int_type = Point{x:3,y:4};
    let float_type = Point{x:4.1,y:4.5}; 
}

单态化

Trait

零成本抽象。

抽象的定义共享的行为

类似接口。

定义
pub trait Summary {
    fn summarize(&self)->String;
}
实现
pub struct NewsArticle{
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}
impl Summary for NewsArticle {
    fn summarize(&self)->String{
        format!("{},by {} ({})",self.headline,self.author,self.location)
    }
}
默认实现
pub trait Summary {
    // fn summarize(&self)->String;
    fn summarize(&self)->String{
        String::from("Read more ..")
    }
}
作为参数
pub fn notify(item : impl Summary) {
    print!("news:{}",item.summarize());
}
pub fn notify1<T:impl Summary + Display>(item:T){
    print!("news:{}",item.summarize());
}
pub fn notify2<T,U>(a:T,b:U) -> String 
where
    T:Summary+Display,
    U:Clone+Debug,
{
    print!("news:{}",a.summarize());
}
作为返回
pub fn notify(s:&str)->impl Summary{
    NewsArticle{
        headline: String::from("震惊!一男子竟独自玩恐怖游戏"),
        location: String::from("四川"),
        author: String::from("张三"),
        content: String::from("ssssss"),
    };
}
fn largest<T:PartialOrd + Copy>(list:&[T])->T{
    let mut largest = list[0];
    for &item in list{
        if item > largest{ // todo 
            largest = item;
        }
    }
    largest
}

生命周期

  • 每个引用都有生命周期
  • 大多数情况是隐式的,可被推断的
  • 手动标志

借用检查器

标注

不影响原生命周期

fn main() {
    let str1 = String::from("adbcd");
    let str2 = "123456";
    println!( "{}",longest(str1.as_str(), str2));
}

fn longest<'a>(x:&'a str,y:&'a str)->&'a str{
    if x.len() >y.len(){
        x
    }else {
        y
    }
}

测试

测试是一个函数,另开一个线程,如果没有panic就通过

3A操作:

  • 准备数据
  • 运行代码
  • 断言结果

测试函数

加上标志

cargo test进行测试

创建

cargo new add –lib

代码

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        let result = 2 + 2;
        assert_eq!(result, 4);
    }
}

运行

cargo test

失败测试样例

#[test]
fn another(){
	panic!("eeeeerror");
}

断言

assert!判断状态是否为空

#[test]
fn is_true(){
    assert!(ret());
}

assert_eq!

assert_nq!

#[test]
fn test_add_two(){
    assert_eq!(5,add_two(4));
}

should_panic

发送恐慌通过测试

#[test]
#[should_panic(expected = "aa")]
fn another(){
    panic!("aba");
}

测试使用Result

#[test]
fn equal()->Result<(),String>{
    if 2+2 == 5{
        Ok(())
    }
    else {
        Err(String::from("error"))
    }
}

test-thread

显示输出

失败println会打印,否则不会。

可以使用

--show-output

按名称运行测试

cargo test add_two
cargo test add

忽略测试

#[ingore]
cargo test --ingored

测试分类

  • 单元测试
  • 集成测试
单元测试
#[cfg(test)]
mod tests {

}

测试私有函数

引入
use super::*

集成测试

创建tests目录

目录下的文件被特殊对待。

use adder;

#[test]
fn it_add_two(){
    assert_eq!(4,adder::add_two(3))
}

其中的帮助函数

创建一个common文件夹

文件夹下面方帮助函数。

实例-命令行程序

grep工具

接受参数

use std::env; //collect


fn main() {
    let args: Vec<String> = env::args().collect();
    let query = &args[1];
    let filename = &args[2];
    println!("worlds: {:?}",query);
    println!("file: {:?}",filename);
}

读取文件

use std::env; //collect
use std::fs;

fn main() {
    let args: Vec<String> = env::args().collect();
    let query = &args[1];
    let filename = &args[2];
    
    println!("worlds: {:?}",query);
    println!("file: {:?}",filename);
    let contents = fs::read_to_string(filename).expect("Something went wrong reading the file");
    println!("{}",contents);
}

重构:改进模块和错误处理

use std::env; //collect
use std::fs;

fn main() {
    let args: Vec<String> = env::args().collect();
    let config = Config::new(&args);
    let contents = fs::read_to_string(config.filename).expect("Something went wrong reading the file");
    println!("{}",contents);
}
struct Config{
    query:String,
    filename:String,
}
impl Config {
    fn new(args:&[String]) -> Config{
        let query = args[1].clone();
        let filename = args[2].clone();
        Config{query,filename}
    }
}

创建lib.rs

use std::fs;
use std::error::Error;


pub struct Config{
    query:String,
    filename:String,
}
impl Config {
    pub fn new(args:&[String]) -> Result<Config,&'static str>{
        if args.len() < 3{
            return  Err("not enough aruguments");
        }
        let query = args[1].clone();
        let filename = args[2].clone();
        Ok(Config{query,filename})
    }
}

pub fn run(config:Config) -> Result<(),Box<dyn Error>>{
    let contents = fs::read_to_string(config.filename)?;
    println!("file contents {}",contents);
    Ok(())
}

main.rs

use std::env; //collect
use std::process;
use grep_project;

fn main() {
    let args: Vec<String> = env::args().collect();
    let config = grep_project::Config::new(&args).unwrap_or_else(|error|{
        println!("Problem parsing arguments:{}",error);
        process::exit(1);
    });
    if let Err(e) = grep_project::run(config){
        println!("Application error:{}",e);
        process::exit(1);
    }
}

使用TDD开发库功能

Test Driven Development

use std::fs;
use std::error::Error;


pub struct Config{
    query:String,
    filename:String,
}
impl Config {
    pub fn new(args:&[String]) -> Result<Config,&'static str>{
        if args.len() < 3{
            return  Err("not enough aruguments");
        }
        let query = args[1].clone();
        let filename = args[2].clone();
        Ok(Config{query,filename})
    }
}

pub fn run(config:Config) -> Result<(),Box<dyn Error>>{
    let contents = fs::read_to_string(config.filename)?;
    for line in search(&config.query, &contents){
        println!("{}",line);
    }
    Ok(())
}
pub fn search<'a>(query:&str,contents:&'a str)->Vec<&'a str>{
    let mut results = Vec::new();
    for line in contents.lines(){
        if line.contains(query){
            results.push(line);
        }
    }
    results
}
#[cfg(test)]
mod tests{
    use super::*;
    #[test]
    fn on_result(){
        let query = "duct";
        let contents = "\
Rust:
safe,fast,productive.
Pick three";
        assert_eq!(vec!["safe,fast,productive."],search(query,contents));
    }
}

使用环境变量

use std::fs;
use std::error::Error;
use std::env; //collect

pub struct Config{
    pub query:String,
    pub filename:String,
    pub case_sensitive:bool,
}
impl Config {
    pub fn new(args:&[String]) -> Result<Config,&'static str>{
        if args.len() < 3{
            return  Err("not enough aruguments");
        }
        let query = args[1].clone();
        let filename = args[2].clone();
        let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
        Ok(Config{query,filename,case_sensitive})
    }
}

pub fn run(config:Config) -> Result<(),Box<dyn Error>>{
    let contents = fs::read_to_string(config.filename)?;
    if config.case_sensitive{
        for line in search(&config.query, &contents){
            println!("{}",line);
        }
    }else {
        for line in search_case_insensitive(&config.query, &contents){
            println!("{}",line);
        }
    }
    
    Ok(())
}
pub fn search<'a>(query:&str,contents:&'a str)->Vec<&'a str>{
    let mut results = Vec::new();
    for line in contents.lines(){
        if line.contains(query){
            results.push(line);
        }
    }
    results
}
pub fn search_case_insensitive<'a>(query:&str,contents:&'a str)->Vec<&'a str>{
    let mut results = Vec::new();
    let query = query.to_lowercase();
    for line in contents.lines(){
        if line.to_lowercase().contains(&query){
            results.push(line);
        }
    }
    results
}
#[cfg(test)]
mod tests{
    use super::*;
    #[test]
    fn case_sensitive(){
        let query = "duct";
        let contents = "\
Rust:
safe,fast,productive.
Pick three
Duck tape"
;
        assert_eq!(vec!["safe,fast,productive."],search(query,contents));
    }
    #[test]
    fn case_insensitive(){
        let query = "rUsT";
        let contents = "\
Rust:
safe,fast,productive.
Pick three
Duck tape
Trust me"
;
        assert_eq!(vec!["Rust:","Trust me"],search_case_insensitive(query,contents));
    }
}

将错误消息写入标准错误

use std::env; //collect
use std::process;
use grep_project;

fn main() {
    let args: Vec<String> = env::args().collect();
    let config = grep_project::Config::new(&args).unwrap_or_else(|error|{
        eprintln!("Problem parsing arguments:{}",error);
        process::exit(1);
    });
    if let Err(e) = grep_project::run(config){
        eprintln!("Application error:{}",e);
        process::exit(1);
    }
   
}


闭包

也叫匿名函数;

可以作为返回值、参数;

可以捕获环境变量;

可以推断输入类型,输出类型;

use std::thread;
use std::time::Duration;
fn main() {
    println!("Hello, world!");
    get_schdule(20);
}
fn get_schdule(time:u32){
    let compute = |args:u32|{
        println!("slowly ....");
        thread::sleep(Duration::from_secs(2));
        args
    };
    if time>10{
        println!("done  .. {}",compute(time));
    }else {
        println!("done .. {}",compute(time));
    }

}

类型推断。

结构体闭包

Fn Trait由标准库提供

所有闭包得实现以下之一:

  • Fn
  • FnMut
  • FnOnce
use std::thread;
use std::time::Duration;
fn main() {
    println!("Hello, world!");
    get_schdule(20);
}

struct  Cacher<T>
where T:Fn(i32)->i32
{
    calculation:T,
    value:Option<i32>,
}
impl <T> Cacher<T>
where T:Fn(i32)->i32
{
    fn new(calculation:T)->Cacher<T>{
        Cacher{
            calculation,
            value:None,
        }
    }
    fn value(&mut self,arg:i32)->i32{
        match self.value {
            Some(v) =>v,
            None=>{
                let v = (self.calculation)(arg);
                self.value = Some(v);
                v
            }
        }
    }
}

fn get_schdule(time:i32){
    
    let mut compute = Cacher::new(|args:i32|{
        println!("slowly ....");
        thread::sleep(Duration::from_secs(2));
        args
    });
    if time>10{
        println!("done  .. {}",compute.value(time));
    }else {
        println!("done .. {}",compute.value(time));
    }

}

Cache限制:只能保留一个值,类型问题

使用哈希map解决;使用模板

use std::thread;
use std::time::Duration;
use std::collections::HashMap;
fn main() {
    println!("Hello, world!");
    get_schdule(20);
    let mut compute = Cacher::new(|args:i32|args);
    println!("{}", compute.value(1));
    println!("{}", compute.value(2));
 
}

struct  Cacher<T>
where T:Fn(i32)->i32
{
    calculation:T,
    value:HashMap<Option<i32>,i32>,
}
impl <T> Cacher<T>
where T:Fn(i32)->i32
{
    fn new(calculation:T)->Cacher<T>{
        Cacher{
            calculation,
            value:HashMap::new(),
        }
    }
    fn value(&mut self,arg:i32)->i32{
        match self.value.get(&Some(arg)){
            Some(v) => *v,
            None =>{
                let v = (self.calculation)(arg);
                self.value.insert(Some(arg), v);
                v
            }
        }  
    }
}

环境捕获

函数不能捕获环境,捕获需要消耗内存。

  • 取得所有权:FnOnce

    
    
  • 取得可变借用:FnMute

    
    
  • 不可变借用:Fn

    
    

使用move可以移动所有权,多用于多线程

filter

过滤条件为false的值。

保留条件为true的值。

#[derive(Debug)]
struct Shoe{
    size:i32,
    style:String,
}
fn shoes_in_my_szie(shoes:Vec<Shoe>,shoe_size:i32)->Vec<Shoe>{
    shoes.into_iter().filter(|x| x.size == shoe_size).collect()
}
fn test(){
    let v = vec![
        Shoe{
            size:10,
            style:String::from("sport"),
        },
        Shoe{
            size:12,
            style:String::from("releax"),
        },
        Shoe{
            size:10,
            style:String::from("clap"),
        },
    ];
    let in_my_size = shoes_in_my_szie(v,10);
    for e in in_my_size.iter(){
        println!("{:#?}",e);
    }
}

迭代器

  • 惰性的
let v1 = vec![1,2,3];
let v1_iter = v1.iter();
for val in v1_iter{
    println!("{}",val);
}

Iterator trait

所有迭代器都实现了 Iterator trait

要有:

  • 返回,item
  • 实现,next->元素的不可变引用

into_iter,取得所有权

iter_mut,可变引用

迭代器的产生与消费

必须实现next,是消耗型的。例如:sum

迭代器适配器,是生产型的。例如map

自定义

struct Counter{
    count:u32,
}
impl Counter {
    fn new()->Counter{
        Counter{
            count:0
        }
    }
}
impl Iterator for Counter {
    type Item = u32;
    fn next(&mut self) ->Option<Self::Item>{
        if self.count < 5{
            self.count +=1;
            Some(self.count)
        }else {
            None                          
        }
    }
}
fn test_diy(){
    let mut counter = Counter::new();
    for i in (0..6){
        println!("{:?}",counter.next());
    }
}

循环与迭代器

迭代器会更快一点。

不产生额外开销。

发布

智能指针

指针:指向变量在内存中的地址

Rust中常用的指针是引用

  • 使用&
  • 借用它指向的值
  • 没有额外开销

引用计数智能指针

  • 记录引用者的数量,让一份数据多个人同时持有
  • 没有使用者自动清理。

String和Vec

使用结构体实现,

Deraf

Drop

Box

实现了

Deraf

Drop

允许在heap上存储数据

在Stack有一点内存,指向heap

可使用与递归

无限大小

use crate::List::{Cons,Nil};
enum List {
    Cons(i32,List),
    Nil,
}
fn box_test(){
    let b = Box::new(5);
    println!("{}",b);
    let list = Cons(1,Cons(2,Cons(3,Nil)));
}

Box解决

use crate::List::{Cons,Nil};
enum List {
    Cons(i32,Box<List>),
    Nil,
}
fn box_test(){
    let b = Box::new(5);
    println!("{}",b);
    let list = Cons(1,Box::new(Cons(2,
        Box::new(Cons(3,Box::new(Nil))))));
}

Deraf

fn test_drefe(){
    let x = 4;
    let y = Box::new(x);
    assert_eq!(4,x);
    assert_eq!(4,*y);
}
use std::ops::Deref;
struct MyBox<T>(T);
impl <T> MyBox<T> {
    fn new(x:T)->MyBox<T>{
        MyBox(x)
    }
}
impl<T> Deref for MyBox<T>  {
    type Target = T;
    fn deref(&self)->&T{
        &self.0
    }
}
fn test_drefe(){
    let x = 4;
    let y = Box::new(x);
    let y = MyBox::new(x);
    assert_eq!(4,x);
    assert_eq!(4,*y);
}

Drop

网络,文件资源释放

不能手动调用

struct CustomSmartPointer{
    data:String,
}
impl Drop for CustomSmartPointer {
    fn drop(&mut self){
        println!("Droping data: {}",self.data);
    }
}
fn test_drop(){
    let c = CustomSmartPointer{
        data:String::from("hello"),
    };
    println!("CustomSmartPointer constructed");
}

可以用标准库的drop,来丢弃。

 drop(c);

Rc

引用计数智能指针

  • 多重所有权
  • 单线程
  • 不在预导入
  • Rc::clone只拷贝引用,不克隆数据
  • 不可变引用
use std::rc::Rc;
enum List {
    Cons(i32,Rc<List>),
    Nil,
}
fn test_rc(){
    let a  = Rc::new(Cons(5,
        Rc::new(Cons(10,
            Rc::new(Nil)))));
    println!("count after creating {}",Rc::strong_count(&a));
    {
        let b = Cons(3,Rc::clone(&a));
        println!("count after creating {}",Rc::strong_count(&a));
    }
    let c = Cons(4,Rc::clone(&a));
    println!("count after creating {}",Rc::strong_count(&a));
}

RefCell

  • 唯一所有权。
  • 可变、不可变借用,在运行时检查。
weak防止内存泄漏

并发

Concurrent:程序的不同部分之间独立的运行

Parallel:程序的不同部分同时运行

Rust无畏并发

多线程

问题

  • 竞争状态
  • 死锁

使用OS,1:1模型,运行时小

语言自己的线程,M:N模型,运行时大

Rust权衡运行时的支持,标准库只提供了1:1模型。

使用spawn创建线程

主线程结束,另一个线程也结束。提供joinhandle等待线程结束才结束。

use std::thread;
use std::time::Duration;
fn main() {
    println!("Hello, world!");
    let handle = thread::spawn(||{
        for i in 1..10{
            println!("h1, number {} from the spawned thread!",i);
            thread::sleep(Duration::from_millis(1))
        }
    });
    // handle.join().unwrap();
    for i in 1..5{
        println!("h1, number {} from the main thread",i);
        thread::sleep(Duration::from_millis(1))
    }
    handle.join().unwrap();

}

move闭包

可以使用其他线程的数据。

fn spawn_move(){
    let v = vec![1,2,4];
    let handle = thread::spawn(move||{
        println!("here's a vector {:?}",v);
    });
    handle.join().unwrap();
}

消息传递

Channel

包含:发送端,接受端

创建

mpsc(多生产者,单消费者)

mpsc::channel

use std::sync::mpsc;
use std::thread;
fn create_channel(){
    let (tx,rx) = mpsc::channel();
    thread::spawn(move ||{
        let val = "hi".to_string();
        tx.send(val).unwrap();
    });
    let received = rx.recv().unwrap();//block
    print!("Got {}",received);
}

try_recv非阻塞。

所有权

  • send后所有权就移动了。

克隆创建多个发送者

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn clone_sender(){
    let (tx,rx) = mpsc::channel();
    let tx1  = mpsc::Sender::clone(&tx);

    thread::spawn(move ||{
        let vals = vec![
            String::from("1: hi"),
            String::from("1: from"),
            String::from("1: the"),
            String::from("1: thread"),
        ];
        for val in vals{
            tx1.send(val).unwrap();
            thread::sleep(Duration::from_millis(100));
        }
    });
    thread::spawn(move ||{
        let vals = vec![
            String::from("2: hi"),
            String::from("2: from"),
            String::from("2: the"),
            String::from("2: thread"),
        ];
        for val in vals{
            tx.send(val).unwrap();
            thread::sleep(Duration::from_millis(100));
        }
    });
    for received in rx{
        println!("Got: {}",received);
    }
}

共享内存并发

channel是单所有权,共享是多所有权

Mutex只允许一个线程来访问某些数据。

通过lock,和unlock来使用

use std::sync::Mutex;
fn create_mutex(){
    let m = Mutex::new(5);
    {
        let mut num = m.lock().unwrap();
        *num = 6;
    }//unlock
    println!("m = {:?}",m);

}
多线程多所有权

Rc要实现send

Arc,原子Rc

use std::sync::{Mutex,Arc};
use std::rc::Rc;
fn create_mutex(){
    let m = Mutex::new(5);
    {
        let mut num = m.lock().unwrap();
        *num = 6;
    }//unlock
    println!("m = {:?}",m);

}
fn mutex_thread(){
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    for _ in 0..10{
        let counter = Arc::clone(&counter);
        let handle = std::thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }
    for handle in handles{
        handle.join().unwrap();
    }
    println!("Result {}",*counter.lock().unwrap());

}

Send Sync trait

send 转移所有权。

几乎所有类型都实现了send

Sync允许多个线程访问

Rc,未实现,Mutex实现了。

面向对象

封装、继承、多态

Rust,封装;没有继承,trait可以复用;多态,泛型和trait约束;

实现面向对象的设计模式

状态模式

pub struct Post{
    state:Option<Box<dyn State>>,
    content:String,
}
impl Post {
    pub fn new()->Post{
        Post{
            state:Some(Box::new(Draft {})),
            content:String::new(),
        }
    }
    pub fn add_text(&mut self,text:&str){
        self.content.push_str(text)
    }
    pub fn content(&self) ->&str{
        self.state.as_ref().unwrap().content(&self)
    }
    pub fn request_review(&mut self){
        if let Some(s) = self.state.take(){
            self.state = Some(s.request_review())
        }
    }
    pub fn approve(&mut self){
        if let Some(s) = self.state.take(){
            self.state = Some(s.approve())
        }
    }

}


trait State {
    fn request_review(self:Box<Self>) ->Box<dyn State>;
    fn approve(self:Box<Self>) ->Box<dyn State>;
    fn content<'a>(&self,post:&'a Post) -> &'a str{
        ""
    }
}
struct Draft {}
impl State for Draft {
    fn request_review(self: Box<Self>) ->Box<dyn State>{
        Box::new(PendingReview {})
    }
    fn approve(self: Box<Self>)->Box<dyn State>{
        self
    }
}

struct PendingReview {}
impl State for PendingReview {
    fn request_review(self: Box<Self>) ->Box<dyn State>{
        self
    }
    fn approve(self: Box<Self>)->Box<dyn State>{
        Box::new(Published{})
    }
}
struct Published {}
 
impl State for Published {
    fn request_review(self: Box<Self>) ->Box<dyn State>{
        self
    }
    fn approve(self: Box<Self>)->Box<dyn State>{
        self
    }
    fn content<'a>(&self,post:&'a Post) -> &'a str{
        &post.content
    }
}
use Blog::Post;

fn main() {
    println!("Hello, world!");
    let mut post  = Post::new();
    post.add_text("I ate a salad for lunch today");

    post.request_review();
    
    post.approve();

}

模式

模式匹配

有,字面值,数值、结构体、枚举、变量、通配符、占位符

使用模式

  • match
  • if let
  • while let
  • for
  • let
  • 函数参数

可辩驳型

无可辩驳的,let x = 5;

可辩驳的 , if let some(v) = a_value;

- 忽略
_x 不会警告
_ 不会绑定
{x,..}
(x,..,y)

match + if

match v{
	4|5|6|7|8 if ..
}

高级特效

不安全Rust

不保证内存安全

  • 解引用原始指针
  • 调用unsafe函数或方法
  • 访问或修改可变静态变量
  • 实现unsafe trait

unsafe仍会检查借用

解引用原始指针

与c交换

*mut T
*const T,这里是类型名的一部分

fn test(){
    let mut num = 5;
    let r1 = &num as *const i32;
    let r2 = &mut num as *mut i32;
    unsafe{
        println!("r1:{}",*r1);
        println!("r2:{}",*r2);
    }
    let address = 0x012345usize;
    let r = address as *const i32;
    unsafe{
        println!("r:{}",*r);
    }
}

高级Trait

pub trait Iterator {
    type Item;
    fn next(&mut self) ->Option<Self::Item>;
}

使用不同的参数可以为一个struct多次实现

重载

#[derive(Debug)]
struct Point{
    x:i32,
    y:i32,
}
use std::ops::Add;
impl Add for Point {
    type Output = Point;
    fn add(self,other:Point)->Point{
        Point{
            x:self.x + other.x,
            y:self.y + other.y,
        }
    }
}
fn test_ops(){
    let p1 = Point{
        x:1,
        y:2
    };
    let p2 = Point{
        x:2,
        y:3,
    };
    println!("{:?}",p1+p2);
}

完全限定语法

<Type as Trait>::function(receiver_if_method,next_arg,..);

类型别名

type elem = i32;//此后elem表示i32类型

函数指针

Fn,FnMut,FnOnce

宏是元编程。

声明宏,快弃用了

过程宏,

  • 自定义派生
  • 属性宏
  • 函数宏

结课项目

单线程


use std::net::{TcpListener,TcpStream};
use std::io::{Read,Write};
use std::fs;
fn main() {
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
    for stream in listener.incoming(){
        let stream = stream.unwrap();
        println!("Connection established!");
        handle_connection(stream);
    }
}
fn handle_connection(mut stream:TcpStream){
    let mut buffer = [0;512];
    stream.read(&mut  buffer).unwrap();
    // println!("Request: {}",String::from_utf8_lossy(&buffer[..]));
    let get = b"GET / HTTP/1.1\r\n";
    let (status_line,filename) = if buffer.starts_with(get){
        ("HTTP/1.1 200 OK\r\n\r\n","hello.html")
    }else {
        ("HTTP/1.1 404 NOT FOUND\r\n\r\n","404.html")
    };
    let response = format!("{}{}",status_line,fs::read_to_string(filename).unwrap());
    stream.write(response.as_bytes()).unwrap();
    stream.flush().unwrap();
}

完整

use std::net::{TcpListener,TcpStream};
use std::io::{Read,Write};
use std::fs;
use std::io::prelude::*;
use std::thread;
use std::time::Duration;
use server::ThreadPool;
fn main() {
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
    let pool = ThreadPool::new(4);
    for stream in listener.incoming().take(2){
        let stream = stream.unwrap();
        pool.excute(||{
            handle_connection(stream);
        });
    }
}
fn handle_connection(mut stream:TcpStream){
    let mut buffer = [0;512];
    stream.read(&mut  buffer).unwrap();
    // println!("Request: {}",String::from_utf8_lossy(&buffer[..]));
    let get = b"GET / HTTP/1.1\r\n";
    let (status_line,filename) = if buffer.starts_with(get){
        ("HTTP/1.1 200 OK\r\n\r\n","hello.html")
    }else {
        ("HTTP/1.1 404 NOT FOUND\r\n\r\n","404.html")
    };
    let response = format!("{}{}",status_line,fs::read_to_string(filename).unwrap());
    stream.write(response.as_bytes()).unwrap();
    stream.flush().unwrap();
}

use std::thread;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;

enum Message {
    NewJob(Job),
    Terminate,
}

pub struct ThreadPool{
    workers:Vec<Worker>,    
    sender:mpsc::Sender<Message>,
}

impl ThreadPool {
    pub fn new(size:usize)->ThreadPool{
        assert!(size>0);
        let (sender,receiver) = mpsc::channel();
        let receiver = Arc::new(Mutex::new(receiver));
        let mut workers = Vec::with_capacity(size);
        for id in 0..size{
            workers.push(Worker::new(id,Arc::clone(&receiver)));
        }
        ThreadPool {workers,sender}
    }
    pub fn excute<F>(&self,f:F)
    where 
        F:FnOnce() + Send +'static,
    {
        let job = Box::new(f);
        self.sender.send(Message::NewJob(job)).unwrap();
    }
}
impl Drop for ThreadPool {
    fn drop(&mut self){
        println!("Sending terminate message to all workers.");

        for _ in &mut self.workers{
            self.sender.send(Message::Terminate).unwrap();
        }
        println!("Shutting down all workers.");
        
        for worker in &mut self.workers{
            println!("Shutting down worker {}",worker.id);
            if let Some(thread) = worker.thread.take(){
                thread.join().unwrap();
            }
            
        } 
    }
}

// struct Job;
type Job = Box<dyn FnBox + Send + 'static>;

struct  Worker{
    id:usize,
    thread:Option<thread::JoinHandle<()>>,
}
trait FnBox {
    fn call_box(self:Box<Self>);
}
impl <F: FnOnce()>FnBox for F {
    fn call_box(self: Box<Self>){
        (*self)();
    }
}

impl Worker {
    fn new(id:usize,receiver:Arc<Mutex<mpsc::Receiver<Message>>>) ->Worker{
        let thread =  thread::spawn(move || loop{
            let message  = receiver.lock().unwrap().recv().unwrap();
            match message{
                Message::NewJob(job) =>{
                    println!("Worker {} got a job;executing",id);
                    job.call_box();

                }
                Message::Terminate =>{
                    println!("Worker {} was told to terminate",id);
                    break;
                }
            };  
        });
        Worker{id,thread:Some(thread)}
    }
}

构建脚本

有c库,链接c库等,生成rust模块

  1. 添加依赖

    [package]
    name = "script_learning"
    version = "0.1.0"
    edition = "2021"
    build = "build.rs"
    # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
    
    [dependencies]
    
    
  2. 编写build.rs

    use std::env;
    use std::fs::File;
    use std::io::Write;
    use std::path::Path;
    
    fn main(){
        let out_dir = env::var("OUT_DIR").unwrap();
        let dest_path = Path::new(&out_dir).join("hello.rs");
        let mut f = File::create(&dest_path).unwrap();
    
        f.write_all(b"
            pub fn say_hello() -> &'static str{
                \"hello\"
            }
        ").unwrap();
    }
    
  3. 调用

    
    include!{concat!(env!("OUT_DIR"),"/hello.rs")}
    
    fn main() {
        println!("{}",say_hello());
    }
    
    

外部

  1. 依赖

    [build-dependencies]
    cc = '1.0'
    
    
  2. build.rs

    extern crate cc;
    fn main(){
        cc::Build::new().file("src/hello.c").compile("hello");
    }
    
  3. hello.c

    #include<stdio.h>
    void hello(){
        printf("hello world 11\n");
    }
    
  4. main.rs

    
    // include!{concat!(env!("OUT_DIR"),"/hello.rs")}
    extern {fn hello();}
    
    fn main() {
        // println!("{}",say_hello());
        unsafe{
            hello();
        }
    }
    
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

都学点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值