[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 幸好发现的早 不然就没有後续ㄌ


<<:  Day 01: ML基础第一步 Python基础入门

>>:  Day 7 Self-attention(一) input和output

选择写程序的路,在AWS上先躲开可能会遇到的地雷区

拆弹教学 接下来的文章,会使用CDK去建置AWS上的服务,在使用CDK之前需要先安装一些程序,才可以...

[Day 01] 你要的全能IDE,Visual Studio Code,它来了!

前言 大家好,我是刚从硕士班毕业不到一年的社会新鲜人,目前担任小小的AI工程师。 兴趣是资料分析和深...

30天程序语言研究

今天是30天程序语言研究的第十六天,由於深度学习老师多让我们上了python的进阶课程里面包括之前没...

[Day18] CH10:排序大家族——合并排序法

今天要介绍的是我们学的最後一个排序法——合并排序法(Merge Sort)。 合并排序法 分成切割与...

[C]makefile范例实作,整理已有的程序码

以现有的程序码做整理 https://github.com/xhbang/C100 整理过後 mai...