iT邦幫忙

2021 iThome 鐵人賽

DAY 3
0
Software Development

Rust的多方面運用系列 第 3

[Day3] Rust 函數 基本 / 進階 使用

那麼最一開始學一個程式語言的起手式想必不用我多說吧。
「Hello World!」

fn hello() {
    println!("Hello World!");
}

那你可能想說,為什麼不是 main function

因為我 source code 放在 Github 上阿。

我打算把它區分清楚這樣查閱的時候也比較方便 owo 。
Github連結
希望有人幫我按星星 owo/


那接下來就是基本的介紹。
Rust 的函數寫法就是

fn function_name(/*引入*/){
    //code
}

那中間就是撰寫 code 的主要位置。

但是如果說是稍微有程式觀念的就會知道我少講了一個東西。

「回傳值格式」

在 Rust 裡回傳值的設定方法就是

fn function_name(/*引入*/) -> /*回傳值形式*/{
    /*code*/
}

回傳方式有兩種

  1. 使用 return
  2. 最後面的東西不加分號

2.可能看起來比較複雜,直接看 code 吧

pub fn add(a: i32, b: i32) -> i32 {
    a + b
}
// 回傳 a + b
pub fn add(a: i32, b: i32) -> i32 {
    a + b
    a += b;
}
// 不合法
pub fn add(a: i32, b: i32) -> i32 {
    return a + b;
    a += b;
}
// 但是可以這樣寫

要注意的是函數也可以作為引數傳入函數中,有點繞口令,還是看 Code 啦

fn into(op: fn(i8, i8) -> i8, a: i8, b: i8) -> i8 {
    op(a, b)
}
fn add(a: i8, b: i8) -> i8 {
    a + b
}
fn product(a: i8, b: i8) -> i8 {
    a * b
}
pub fn oao() {
    let a = 5;
    let b = 4;
    println!("{}", into(add, a, b));
    println!("{}", into(product, a, b));
}

那可能有些人,會特別在意速度,那其實 Rust 也有 CTFE (Compile Time Function Evaluation)
也就是編譯期處理,把原本要運行的東西拉入編譯中處理。
我自己是在學 Rust 前不知道這個東西,所以不確定其他語言有沒有
寫法如下

pub const fn CTFE() -> i8 {
    5
}

就前面加一個 const 即可,我自己測試 Stable 版本中已經可用了


以下是函數進階篇

隱藏函數

pub fn owo(){
    println!("1");
}
pub fn func() {
    owo();
    {
        owo();
        fn owo(){
            println!("2");
        }
    }
}

具體輸出為

那可以發現說 他的函數會先吃同樣的範圍的。

而不會吃外面的,但是我也不會這樣寫。

可讀性有夠差 感覺混淆程式碼很好用

回傳多項

在 Rust 中可以一次回傳多項。

就像是

pub fn retmany() -> (i32, i32) {
    (5, 8)
}

這樣的話其實他的回傳值就有兩個,但是相對的,也要靠兩個變數去接

就像是這樣

pub fn return_many() {
    let (a, b) = function::retmany();
    println!("{} {}", a, b);
}

就能接到 a 跟 b 兩個值

底線可以用來忽略傳入的東西

如果這樣寫

pub fn uninput(_: i32) {
    println!("{}", _);
}

並使用,會報錯

error: in expressions, `_` can only be used on the left-hand side of an assignment
  --> src/Basic/function.rs:21:20
   |
21 |     println!("{}", _);
   |                    ^ `_` not allowed here

也就是可以讓傳入的東西不能用,應該在後面的 Bevy Engine 會用到。

泛型函數

先解釋一下泛型函數的使用用途。

今天如果說,要寫一個能使用多型別 Ex: float, Int 的程式,
那可能你就要重複寫兩個函數,差別只有一個是 Float 型式的一個是 Int 型式的

為了解決這種東西,泛型函數誕生了,而以下的使用方法我是以官方 Rust Book 的文檔來寫
因為我想不到該怎麼解釋 QwQ

pub fn Tfunc<T>(a: &[T]) {

}

以上的代碼就是泛型的使用方式,那只要這樣就能傳入不同型式了。

但是,有時後會遇到問題。

pub fn Tfunc<T>(a: &[T]) -> T {
    let mut largest = a[0];
    for &i in a.iter() {
        if i < largest {
            largest = i;
        }
    }
    largest
}

假如說這樣編譯的話,會出現

這樣的錯誤,主要是由於使用了 "<" 所導致的

這是由於如果要做大於小於的判斷,必須要實做
PartialOrd 特性,但是因為使用了泛型,所以這個型別不一定會使用此特性,
故需要讓傳入的 T 有這個特性

pub fn Tfunc<T: PartialOrd>(a: &[T]) -> T {
    let mut largest = a[0];
    for &i in a.iter() {
        if i < largest {
            largest = i;
        }
    }
    largest
}

但是即使這樣編譯器還是會報錯,這是因為
使用了largest這個變數,將 a 陣列中的值指派給了其他變數,所以也必須實現
Copy 那添加方式則是在 PartialOrd 寫個 + 號後面再加 Copy。

最後的程式會像這樣

pub fn Tfunc<T: PartialOrd + Copy>(a: &[T]) -> T {
    let mut largest = a[0];
    for &i in a.iter() {
        if i < largest {
            largest = i;
        }
    }
    largest
}

這樣就編譯成功了!

那其實呼叫的方式就是直接讓它判斷傳入的值是什麼。

Ex:

pub fn Tcall() {
    let int_list = vec![34, 50, 25, 100, 65];
    function::Tfunc(&int_list);
}

或者說,也能使用 turbofish 的語法,讓編譯器可以先判斷是什麼型別,
不過即使使用了 turbofish 還是要在 T 上加 PartialOrd 跟 Copy 的特性
Ex:

pub fn Tcall() {
    let int_list = vec![34, 50, 25, 100, 65];
    function::Tfunc::<i32>(&int_list);
}

那可能你也會想說,一定要用 T 嗎?

答案是,其實不用。

而這也代表了,泛型不只能使用一種型別的變數。

也就是可以定義一個 T 是 i32 一個 W 是 i64


今天的內容可能有些難澀,我自己也不常使用。
但是蠻有趣和好玩的,學起來總是有會用到的時候。
明天我想講一些簡單的閉包和基礎的運算 if else match
有問題可以直接提問喔 owob
然後小小抱怨一下,我早上存的草稿竟然不見了 QQQQQ 幸好發現的早 不然就沒有後續ㄌ


上一篇
[Day2] 安裝 Rust
下一篇
[Day4] Rust 閉包以及判斷式
系列文
Rust的多方面運用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言