亚洲AVI,黑人巨茎大战欧美白妇,初高中生洗澡自慰高清网站,欧美日韩无砖专区一中文字

重慶分公司,新征程啟航

為企業(yè)提供網(wǎng)站建設(shè)、域名注冊、服務(wù)器等服務(wù)

用Rust寫一個鏈表,非常詳細(xì),一遍看懂-創(chuàng)新互聯(lián)

前言

在Rust里寫一個鏈表可不是一件容易的事,涉及到很多的知識點(diǎn),需要熟練掌握之后才能寫出一個不錯的鏈表。這篇文章主要介紹了如何寫一個Rust鏈表,并且補(bǔ)充了涉及到的很多的額外知識點(diǎn),尤其是所有權(quán)問題。
首先,你需要明白,為什么Rust鏈表難寫,同樣的為什么C實(shí)現(xiàn)簡單一點(diǎn)呢?
只能有一個引用?。?!這是最關(guān)鍵的,然后就是Rust中是沒有NULL指針的,這就需要用到Option枚舉,在編譯階段必須知道類型的大小,這就需要使用Box智能指針。
在C語言實(shí)現(xiàn)一個簡單的鏈表,可以這樣寫:

創(chuàng)新互聯(lián)長期為上千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為點(diǎn)軍企業(yè)提供專業(yè)的網(wǎng)站設(shè)計制作、成都網(wǎng)站制作,點(diǎn)軍網(wǎng)站改版等技術(shù)服務(wù)。擁有十年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
Node* new_node = create_new_node(v);
new_node->next = head;
head = new_node;

在這里插入圖片描述
這段代碼里面,head和new_node->next指向了同一個節(jié)點(diǎn),這個在C語言里面沒事,但是在Rust不允許,因為指針類型為Box,Box對象同一時刻只能有一個可變引用,而在上面的插入過程中,第2行,有兩個指針指向同一個頭結(jié)點(diǎn)。

預(yù)先知識點(diǎn) Option枚舉及其所有權(quán)問題

1.最簡單的Option枚舉就是,里面有Some和None,對于枚舉大家一定要了解,他是等于其中一個的,并不是包含關(guān)系,這一點(diǎn)一定要理清楚,不然代碼會很難理解。None可以替代C語言中的NULL。

pub enum Option{Some(T),
    None,
}

2.unwrap()方法:
在確認(rèn)Option不為None的情況下,可以用unwrap方法拆解出其中的值,并獲取值的所有權(quán),如果為None,就會引發(fā)panic。這里要強(qiáng)調(diào)的是,unwrap會消費(fèi)Option本身的值,后面就不能再用了。

let a = Some(Box::new(5));
    let d = a.unwrap();
    // println!("{:?}", a); // cannot use 'a' again.
    println!("{:?}", d);

但這里有一個所有權(quán)的限制,因為涉及到其內(nèi)部值的所有權(quán)的轉(zhuǎn)移,所以只有Option原始變量本身可以調(diào)用unwrap方法,其引用(包括可變引用)均不可調(diào)用。這和unwrap的實(shí)現(xiàn)方法有關(guān)系,因為其消費(fèi)了原始變量。下方代碼不可編譯:

let mut a = Some(Box::new(5));
    let b = &mut a;
    let c = b.unwrap(); // Error! cannot move out of `*b` which is behind a mutable reference

下面是unwrap源碼:傳入self,會轉(zhuǎn)移所有權(quán)。

pub const fn unwrap(self) ->T {match self {Some(val) =>val,
            None =>panic!("called `Option::unwrap()` on a `None` value"),
        }

3.take()方法:
take方法可以從Option中取出值,為原來的Option變量留下None值,但原來的變量還有效(但實(shí)際上沒什么用處了,因為只剩下None了)。take方法的原始值或者引用必須為mut類型。強(qiáng)調(diào)一下,借用情況下可以調(diào)用take方法?;蛘哒f指向Option的可變引用(指針)可以調(diào)用take方法。

fn main() {let mut a = Some(Box::new(5));
    let mut b = &mut a;
    let c = &mut b;
    let d = c.take();
    println!("{:?}", c);//None
    println!("{:?}", d);//Some(5)
    println!("{:?}", a);//None
}

4.as_ref()方法:
但上面這兩個方法,都改變了Option變量本身。如果不改變或受到限制無法改變Option本身時,只想借用或可變借用其中unwrap的值應(yīng)該怎么辦呢?這也是在實(shí)現(xiàn)鏈表時遇到的讓人掉坑的問題。幸好Option提供了 as_ref 和 as_mut方法。

fn as_ref(&self) ->Option<&T>//接受一個不可變引用,不獲取所有權(quán),返回一個Option枚舉,其中的T為不可變引用類型,不獲取所有權(quán)。

該方法將對Option的引用變?yōu)閷ption所包含對象的不可變引用,并且返回一個新的Option。對這個新的Option進(jìn)行unwrap操作,可以獲得原Option所包含的對象的不可變引用。原始Option變量及其引用,均可調(diào)用as_ref方法,有點(diǎn)克隆的味道。例如:

let a = Some(Box::new(5));
    let b = a.as_ref();
    let c = b.unwrap();
    println!("{:?}", a);//Some(5)
    println!("{:?}", b);//Some(5)
    println!("{:?}", c);//5

5.as_mut()方法
與as_ref類似,as_mut只是返回了Option包含的數(shù)據(jù)的可變引用,

fn main() {let mut a = Some(Point {x: 5, y: 5 });
    // let b = a.as_mut();
    let b = &mut a;
    let c = b.as_mut(); // c is a new Option;
    let d = c.unwrap();
    d.x += 10;
    let e = &mut d.y;
    *e += 20;
 
    println!("{:?}", d.x);//15
    // println!("{:?}", c); // c is not available because of method call of "unwrap".
    println!("{:?}", b);//Some(Point { x: 15, y: 25 })
    println!("{:?}", a);//Some(Point { x: 15, y: 25 })
}

通過以上代碼可以看出,在未改變Option變量a的情況下,通過as_mut方法,改變了其包含的數(shù)據(jù)的值。這個能力對于編寫鏈表時,尤其是節(jié)點(diǎn)的插入、刪除時,可以靈活的操作指向下一個節(jié)點(diǎn)的指針。
說明一點(diǎn),調(diào)用as_mut方法時,Option變量本身或其引用,必須為可變的,即mut類型。
簡單類型的一個例子:這里注意原來的5已經(jīng)改變成15。

fn main() {let mut a = Some(5);
    let b = a.as_mut().unwrap();
    *b += 10;
    println!("b = {:?}", b);//b = 15
    println!("a = {:?}", a);//a=Some(15)
}
if let和while let還有?操作符
if let Some(a_T) = a_option {  //a_T生命周期僅在花括號內(nèi)生效。
  //當(dāng)a_option不為None時滿足條件。
}

 while let Some(a_T) = a_option {  //當(dāng)a_option 為None時退出循環(huán)。
}

?操作符的作用就是簡化Result枚舉的,舉個例子:

let f = File::open("username.txt");
let mut f = match f {Ok(file) =>file,
    Err(e) =>return Err(e),
};
//有了?運(yùn)算符,就可以改成下面一行話
let mut f = File::open("username.txt")?;
Rust方法和關(guān)聯(lián)函數(shù)

具體可以看https://blog.csdn.net/cacique111/article/details/126311569,說的很詳細(xì)。

如何編寫一個鏈表 鏈表的定義

鏈表比較繞圈子,回顧Rust內(nèi)存管理的基礎(chǔ)知識,Rust需要在編譯時知道一個類型占用多少空間,Node結(jié)構(gòu)體內(nèi)部嵌套了它自己,這樣在編譯時就無法確認(rèn)其占用空間大小了。 在Rust中當(dāng)有一個在編譯時未知大小的類型,而又想要在需要確切大小的上下文中使用這個類型值的時候,可以使用智能指針Box。

/// 單鏈表節(jié)點(diǎn)
#[derive(Debug)]
struct Node{val: T,
    next: Option>>,
}
/// 單鏈表
#[derive(Debug)]
struct LinkedList{head: Option>>,
    size: usize,
}

上面的代碼定義之后,在Rust里面經(jīng)常使用New函數(shù),所以下面寫上面結(jié)構(gòu)體的new方法,

implLinkedList{pub fn new() ->Self {SimpleLinkedList {head: None,
            size: 0,
        }
    }

LinkedList的new函數(shù)用來創(chuàng)建一個空鏈表。

常用的鏈表操作
pub fn is_empty(&self) ->bool {self.size == 0
    }
    pub fn len(&self) ->usize {self.size
    }
    //頭插法
    pub fn push(&mut self, data: T)->&mut Self {let node = Box::new(Node::{val: data,
            next: self.head.take(),//如果是空鏈表的話,直接為None,要不然就是Some(T)。為什么這里不用self.head?
            //看上去一樣,但是如果不take,就會出現(xiàn)多個指針指向同一個節(jié)點(diǎn),不符合所有權(quán)規(guī)則,可變引用可以進(jìn)行take
            //take之后就會釋放,變成None
        });
        self.head = Some(node);//將“頭指針”指向新插入的節(jié)點(diǎn)
        self.size += 1;
        self
    }
//pop會從頭節(jié)點(diǎn)開始刪除節(jié)點(diǎn),返回例如Some(5),Option,T的所有權(quán)轉(zhuǎn)移,刪除轉(zhuǎn)移也沒啥。
    pub fn pop(&mut self) ->Option{if self.is_empty() {return None;
        }
        let head = self.head.take().unwrap();//取出Some里面的值
        self.head = head.next;//head指向下一個節(jié)點(diǎn)
        self.size -= 1;
        Some(head.val)
    }
    //peek返回頭節(jié)點(diǎn),是T的引用,不轉(zhuǎn)移所有權(quán)。
    pub fn peek(&self) ->Option<&T>{if self.is_empty() {return None
        }
        return Some(&self.head.as_ref().unwrap().val)
        //這里確實(shí),只是返回一個引用,不能對原來的鏈表造成任何影響。
    }
    pub fn peek_mut(&mut self) ->Option<&mut T>{if self.is_empty() {return None
        }//這里就是as_mut,外部改變T的值,鏈表里面的值也會受到改變。
        return Some(&mut self.head.as_mut().unwrap().val)
    }
pub fn rev(mut self) ->SimpleLinkedList{let mut rev_list = SimpleLinkedList::::new();
        while !self.is_empty() {rev_list.push(self.pop().unwrap());
        }
        rev_list
    }

main函數(shù)部分,其中迭代器部分下期會有詳細(xì)的解答。

fn main(){let mut list=SimpleLinkedList::new();
    list.push(3);
    list.push(2);
    list.push(1);
    list.len();
    // println!("{list:?}");
    // for val in list.from_iter() {//     println!("{}",val);
    // }
    // for val in list.iter_mut() {//     *val += 1;
    //     println!("{}",val);
    // }
    for val in list.into_iter() {println!("{}",val);
    }

    //println!("{:?}",list.pop());
    //list.pop();
    //println!("{list:?}");
    //println!("{}",list.len());
}

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧


標(biāo)題名稱:用Rust寫一個鏈表,非常詳細(xì),一遍看懂-創(chuàng)新互聯(lián)
文章網(wǎng)址:http://news.spvevtbd.cn/article/cedogi.html

其他資訊

在線咨詢
服務(wù)熱線
服務(wù)熱線:028-86922220
TOP