【Rust】枚舉和模式匹配

目錄

  • 枚舉和模式匹配
    • 枚舉的定義
    • Option 枚舉
    • 控制流運算符 match
    • 簡潔控制流 if let

枚舉和模式匹配

枚舉的定義

結構體給予你將字段和數據聚合在一起的方法,像 Rectangle 結構體有 widthheight 兩個字段。而枚舉給予你一個途徑去聲明某個值是一個集合中的一員。

假設我們要處理 IP 地址。目前被廣泛使用的兩個主要 IP 標準:IPv4 和 IPv6。這是程序可能會遇到的所有可能的 IP 地址類型:所以可以枚舉出所有可能的值,這也正是此枚舉名字的由來。

任何一個 IP 地址要么是 IPv4 的要么是 IPv6 的,而且不能兩者都是。IP 地址的這個特性使得枚舉數據結構非常適合這個場景,因為枚舉值只可能是其中一個成員。IPv4 和 IPv6 從根本上講仍是 IP 地址,所以當代碼在處理適用于任何類型的 IP 地址的場景時應該把它們當作相同的類型。

可以通過在代碼中定義一個 IpAddrKind 枚舉來表現這個概念并列出可能的 IP 地址類型,V4V6。這被稱為枚舉的 成員

enum IpAddrKind {V4,V6,
}

現在 IpAddrKind 就是一個可以在代碼中使用的自定義數據類型了。這樣就可以創建 IpAddrKind 兩個不同成員的實例:

let four = IpAddeKind::V4;
let six = IpAddeKind::V6;

注意枚舉的成員位于其標識符的命名空間中,并使用兩個冒號分開。這么設計的益處是現在 IpAddrKind::V4IpAddrKind::V6 都是 IpAddrKind 類型的。例如,接著可以定義一個函數來接收任何 IpAddrKind類型的參數,使用任一成員來調用這個函數:

enum IpAddrKind {V4,V6,
}fn main() {let four = IpAddrKind::V4;let six = IpAddrKind::V6;route(IpAddrKind::V4);route(IpAddrKind::V6);
}fn route(ip_kind: IpAddrKind) {}

使用枚舉甚至還有更多優勢。進一步考慮一下我們的 IP 地址類型,目前沒有一個存儲實際 IP 地址數據的方法;只知道它是什么類型的。可以使用結構體來解決這個問題:

#[derive(Debug)]
enum IpAddrKind {V4,V6,
}#[derive(Debug)]
struct IpAddr {kind: IpAddrKind,address: String,
}fn main() {let home = IpAddr {kind: IpAddrKind::V4,address: String::from("127.0.0.1"),};let loopback = IpAddr {kind: IpAddrKind::V6,address: String::from("::1"),};println!("{:?}", home);println!("{:?}", loopback);
}

這里定義了一個有兩個字段的結構體 IpAddrIpAddrKind(之前定義的枚舉)類型的 kind字段和 String 類型 address 字段。有這個結構體的兩個實例。第一個,home,它的 kind的值是 IpAddrKind::V4 與之相關聯的地址數據是 127.0.0.1。第二個實例,loopbackkind的值是 IpAddrKind 的另一個成員,V6,關聯的地址是 ::1。使用了一個結構體來將 kindaddress 打包在一起,現在枚舉成員就與值相關聯了。

還可以使用一種更簡潔的方式來表達相同的概念,僅僅使用枚舉并將數據直接放進每一個枚舉成員而不是將枚舉作為結構體的一部分。IpAddr 枚舉的新定義表明了 V4V6 成員都關聯了 String值:

#[derive(Debug)]
enum IpAddrKind {V4(String),V6(String),
}fn main() {let home = IpAddrKind::V4(String::from("127.0.0.1"));let loopback = IpAddrKind::V6(String::from("::1"));println!("{:?}", home);println!("{:?}", loopback);
}

直接將數據附加到枚舉的每個成員上,這樣就不需要一個額外的結構體了。這里也很容易看出枚舉工作的另一個細節:每一個定義的枚舉成員的名字也變成了一個構建枚舉的實例的函數。也就是說,IpAddr::V4() 是一個獲取 String 參數并返回 IpAddr 類型實例的函數調用。作為定義枚舉的結果,這些構造函數會自動被定義。

用枚舉替代結構體還有另一個優勢:每個成員可以處理不同類型和數量的數據。IPv4 版本的 IP 地址總是含有四個值在 0 和 255 之間的數字部分。如果想要將 V4 地址存儲為四個 u8 值而 V6 地址仍然表現為一個 String,這就不能使用結構體了。枚舉則可以輕易的處理這個情況:

#[derive(Debug)]
enum IpAddrKind {V4(u8, u8, u8, u8),V6(String),
}fn main() {let home = IpAddrKind::V4(127,0,0,1);let loopback = IpAddrKind::V6(String::from("::1"));println!("{:?}", home);println!("{:?}", loopback);
}

這些代碼展示了如何用枚舉來表示兩種類型的 IP 地址。雖然這種做法是有效的,但由于存儲和處理 IP 地址在實際開發中非常常見,Rust 的標準庫早已為我們提供了一個現成的解決方案。標準庫中的 IpAddr 枚舉與我們自定義的非常相似,但它更進一步:將每種 IP 類型分別封裝在專門的結構體中,從而更清晰地區分不同格式的 IP 地址:

#![allow(unused)]
fn main() {
struct Ipv4Addr {// --snip--
}struct Ipv6Addr {// --snip--
}enum IpAddr {V4(Ipv4Addr),V6(Ipv6Addr),
}
}

這些代碼展示了可以將任意類型的數據放入枚舉成員中:例如字符串、數字類型或者結構體。甚至可以包含另一個枚舉!另外,標準庫中的類型通常并不比你設想出來的要復雜多少。

注意雖然標準庫中包含一個 IpAddr 的定義,仍然可以創建和使用我們自己的定義而不會有沖突,因為我們并沒有將標準庫中的定義引入作用域。

枚舉的成員中可以內嵌多種多樣的類型:

enum Message {Quit,Move { x: i32, y: i32 },Write(String),ChangeColor(i32, i32, i32),
}

這個枚舉有四個含有不同類型的成員:

  • Quit 沒有關聯任何數據。
  • Move 類似結構體包含命名字段。
  • Write 包含單獨一個 String
  • ChangeColor 包含三個 i32

定義一個這樣的有關聯值的枚舉的方式和定義多個不同類型的結構體的方式很相像,除了枚舉不使用 struct 關鍵字以及其所有成員都被組合在一起位于 Message 類型下。如下這些結構體可以包含與之前枚舉成員中相同的數據:

struct QuitMessage; // 類單元結構體
struct MoveMessage {x: i32,y: i32,
}
struct WriteMessage(String); // 元組結構體
struct ChangeColorMessage(i32, i32, i32); // 元組結構體

不過,如果使用不同的結構體,由于它們都有不同的類型,將不能像使用定義的 Message 枚舉那樣,輕易的定義一個能夠處理這些不同類型的結構體的函數,因為枚舉是單獨一個類型。

結構體和枚舉還有另一個相似點:就像可以使用 impl 來為結構體定義方法那樣,也可以在枚舉上定義方法。這是一個定義于 Message 枚舉上的叫做 call 的方法:

#[derive(Debug)]
enum Message {Quit,Move { x: i32, y: i32 },Write(String),ChangeColor(i32, i32, i32),
}impl Message {fn call(&self) {println!("{:?}", self)}
}fn main() {let m = Message::Write(String::from("hello"));m.call();
}

方法體使用了 self 來獲取調用方法的值。這個例子中,創建了一個值為 Message::Write(String::from("hello")) 的變量 m,而且這就是當 m.call() 運行時 call 方法中的 self 的值。

Option 枚舉

Option 是標準庫定義的另一個枚舉。Option 類型應用廣泛因為它編碼了一個非常普遍的場景,即一個值要么有值要么沒值。

例如,如果請求一個非空列表的第一項,會得到一個值,如果請求一個空的列表,就什么也不會得到。從類型系統的角度來表達這個概念就意味著編譯器需要檢查是否處理了所有應該處理的情況,這樣就可以避免在其他編程語言中非常常見的 bug。

編程語言的設計經常要考慮包含哪些功能,但考慮排除哪些功能也很重要。Rust 并沒有很多其他語言中有的空值功能。空值是一個值,它代表沒有值。在有空值的語言中,變量總是這兩種狀態之一:空值和非空值。

空值的問題在于當你嘗試像一個非空值那樣使用一個空值,會出現某種形式的錯誤。因為空和非空的屬性無處不在,非常容易出現這類錯誤。

然而,空值嘗試表達的概念仍然是有意義的:空值是一個因為某種原因目前無效或缺失的值。

問題不在于概念而在于具體的實現。為此,Rust 并沒有空值,不過它確實擁有一個可以編碼存在或不存在概念的枚舉。這個枚舉是 Option<T>,而且它定義于標準庫中,如下:

enum Option<T> {None,Some(T),
}

Option<T> 枚舉是如此有用以至于它甚至被包含在了 prelude 之中,你不需要將其顯式引入作用域。另外,它的成員也是如此,可以不需要 Option:: 前綴來直接使用 SomeNone。即便如此 Option<T> 也仍是常規的枚舉,Some(T)None 仍是 Option<T> 的成員。

<T> 是一個泛型類型參數,目前,只需要知道的就是 <T> 意味著 Option 枚舉的 Some 成員可以包含任意類型的數據,同時每一個用于 T 位置的具體類型使得 Option<T> 整體作為不同的類型。這里是一些包含數字類型和字符串類型 Option 值的例子:

fn main() {let some_number = Some(5);let some_string = Some("a string");let absent_number: Option<i32> = None;println!("{:?}", some_number);println!("{:?}", some_string);println!("{:?}", absent_number);
}

some_number 的類型是 Option<i32>some_char 的類型是 Option<char>,是不同于some_number的類型。因為在 Some 成員中指定了值,Rust 可以推斷其類型。對于 absent_number,Rust 需要指定 Option 整體的類型,因為編譯器只通過 None 值無法推斷出 Some 成員保存的值的類型。這里我們告訴 Rust 希望 absent_numberOption<i32> 類型的。

當有一個 Some 值時,就知道存在一個值,而這個值保存在 Some 中。當有個 None 值時,在某種意義上,它跟空值具有相同的意義:并沒有一個有效的值。那么,Option<T> 為什么就比空值要好呢?

簡而言之,因為 Option<T>T(這里 T 可以是任何類型)是不同的類型,編譯器不允許像一個肯定有效的值那樣使用 Option<T>。例如,這段代碼不能編譯,因為它嘗試將 Option<i8>i8相加:

fn main() {let x: i8 = 5;let y: Option<i8> = Some(5);let sum = x + y;
}

運行這段代碼將會產生錯誤信息:

error[E0277]: cannot add `Option<i8>` to `i8`--> src/main.rs:46:17|
46 |     let sum = x + y;|                 ^ no implementation for `i8 + Option<i8>`|= help: the trait `Add<Option<i8>>` is not implemented for `i8`= help: the following other types implement trait `Add<Rhs>`:`&i8` implements `Add<i8>``&i8` implements `Add``i8` implements `Add<&i8>``i8` implements `Add`

這意味著 Rust 不知道該如何將 Option<i8>i8 相加,因為它們的類型不同。當在 Rust 中擁有一個像 i8 這樣類型的值時,編譯器確保它總是有一個有效的值。這樣可以自信使用而無需做空值檢查。只有當使用 Option<i8>(或者任何用到的類型)的時候需要擔心可能沒有值,而編譯器會確保在使用值之前處理了為空的情況。

換句話說,在對 Option<T> 進行運算之前必須將其轉換為 T。通常這能幫助開發者捕獲到空值最常見的問題之一:假設某值不為空但實際上為空的情況。

Option 枚舉最常見的應用場景就是函數可能返回空值,如下面代碼所示:

fn find_user(id: u32) -> Option<String> {if id == 1 {Some("Alice".to_string())} else {None}
}fn main() {if let Some(name) = find_user(1) {println!("User: {}", name);} else {println!("User not found.");}
}

用途: 比如查數據庫、查列表、查配置時,找不到返回 None,找到了返回 Some(value)

控制流運算符 match

Rust 有一個叫做 match 的極為強大的控制流運算符,它允許我們將一個值與一系列的模式相比較,并根據相匹配的模式執行相應代碼。模式可由字面值、變量、通配符和許多其他內容構成。match 的力量來源于模式的表現力以及編譯器檢查,它確保了所有可能的情況都得到處理

可以把 match 表達式想象成某種硬幣分類器:硬幣滑入有著不同大小孔洞的軌道,每一個硬幣都會掉入符合它大小的孔洞。同樣地,值也會通過 match 的每一個模式,并且在遇到第一個 “符合” 的模式時,值會進入相關聯的代碼塊并在執行中被使用:

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 => 25,}
}fn main() {println!("{}", value_in_cents(Coin::Penny));println!("{}", value_in_cents(Coin::Nickel));println!("{}", value_in_cents(Coin::Dime));println!("{}", value_in_cents(Coin::Quarter));
}

拆開 value_in_cents 函數中的 match 來看。首先,列出 match 關鍵字后跟一個表達式,在這個例子中是 coin 的值。這看起來非常像 if 所使用的條件表達式,不過這里有一個非常大的區別:對于 if,表達式必須返回一個布爾值,而這里它可以是任何類型的。

接下來是 match 的分支。一個分支有兩個部分:一個模式和一些代碼。第一個分支的模式是值 Coin::Penny 而之后的 => 運算符將模式和將要運行的代碼分開。每一個分支之間使用逗號分隔。

match 表達式執行時,它將結果值按順序與每一個分支的模式相比較。如果模式匹配了這個值,這個模式相關聯的代碼將被執行。如果模式并不匹配這個值,將繼續執行下一個分支,非常類似一個硬幣分類器。可以擁有任意多的分支。

每個分支相關聯的代碼是一個表達式,而表達式的結果值將作為整個 match 表達式的返回值。

如果分支代碼較短的話通常不使用大括號,正如每個分支都只是返回一個值。如果想要在分支中運行多行代碼,可以使用大括號,而分支后的逗號是可選的。例如,如下代碼在每次使用Coin::Penny 調用時都會打印出 “Lucky penny!”,同時仍然返回代碼塊最后的值,1

fn value_in_cents(coin: Coin) -> u8 {match coin {Coin::Penny => {println!("Lucky penny!");1}Coin::Nickel => 5,Coin::Dime => 10,Coin::Quarter => 25,}
}

匹配分支的另一個有用的功能是可以綁定匹配的模式的部分值。

1999 年到 2008 年間,美國在 25 美分的硬幣的一側為 50 個州的每一個都印刷了不同的設計。其他的硬幣都沒有這種區分州的設計,所以只有這些 25 美分硬幣有特殊的價值。可以將這些信息加入一個 enum,通過改變 Quarter 成員來包含一個 State 值:

#[derive(Debug)]
enum UsState {Alabama,Alaska,
}enum Coin {Penny,Nickel,Dime,Quarter(UsState),
}fn value_in_cents(coin: Coin) -> u8 {match coin {Coin::Penny => 1,Coin::Nickel => 5,Coin::Dime => 10,Coin::Quarter(state) => {println!("State quarter from {:?}!", state);25}}
}fn main() {let value = value_in_cents(Coin::Quarter(UsState::Alaska));println!("{:?}", value);
}

如果調用 value_in_cents(Coin::Quarter(UsState::Alaska))coin 將是 Coin::Quarter(UsState::Alaska)。當將值與每個分支相比較時,沒有分支會匹配,直到遇到 Coin::Quarter(state)。這時,state 綁定的將會是值 UsState::Alaska。接著就可以在 println! 表達式中使用這個綁定了,像這樣就可以獲取 Coin 枚舉的 Quarter 成員中內部的州的值。

在之前的部分中使用 Option<T> 時,是為了從 Some 中取出其內部的 T 值;還可以像處理 Coin 枚舉那樣使用 match 處理 Option<T>,只不過這回比較的不再是硬幣,而是 Option<T>的成員,但 match 表達式的工作方式保持不變。

比如想要編寫一個函數,它獲取一個 Option<i32> ,如果其中含有一個值,將其加一。如果其中沒有值,函數應該返回 None 值,而不嘗試執行任何操作:

fn main() {let five = Some(5);let six = plus_one(five);let none = plus_one(None);println!("{:?}, {:?}, {:?}", five, six, none);
}
fn plus_one(x: Option<i32>) -> Option<i32> {match x {None => None,Some(i) => Some(i + 1),}
}

match 還有另一方面需要討論:這些分支必須覆蓋了所有的可能性。考慮一下 plus_one 函數的這個版本,它有一個 bug 并不能編譯:

fn main() {let five = Some(5);let six = plus_one(five);let none = plus_one(None);println!("{:?}, {:?}, {:?}", five, six, none);
}
fn plus_one(x: Option<i32>) -> Option<i32> {match x {Some(i) => Some(i + 1),}
}

沒有處理 None 的情況,所以這些代碼會造成一個 bug。幸運的是,這是一個 Rust 知道如何處理的 bug。如果嘗試編譯這段代碼,會得到這個錯誤:

error[E0004]: non-exhaustive patterns: `None` not covered--> src/main.rs:8:11|
8   |     match x {|           ^ pattern `None` not covered|
note: `Option<i32>` defined here--> /Users/huangruibang/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs:572:1|
572 | pub enum Option<T> {| ^^^^^^^^^^^^^^^^^^
...
576 |     None,|     ---- not covered= note: the matched value is of type `Option<i32>`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown|
9   ~         Some(i) => Some(i + 1),
10  ~         None => todo!(),|

Rust 知道沒有覆蓋所有可能的情況甚至知道哪些模式被忘記了。Rust 中的匹配是窮盡的:必須窮舉到最后的可能性來使代碼有效。特別的在這個 Option<T> 的例子中,Rust 防止開發者忘記明確的處理 None 的情況,這讓開發者免于假設擁有一個實際上為空的值,從而使錯誤不可能發生。

有時候只需要對特定的值采取特殊操作,其他的值采取默認操作,就可以通過通配模式——將匹配到的默認值綁定為 other 來實現。例如:只在 1、3、5、7 的時候有輸出,其它數字都不進行操作:

fn main() {let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];for element in arr {match element {1 => println!("One"),3 => println!("Three"),5 => println!("Five"),7 => println!("Seven"),other => println!("Other"),}}
}

除了用通配模式,還可以用占位符 _ 來實現:

fn main() {let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];for element in arr {match element {1 => println!("One"),3 => println!("Three"),5 => println!("Five"),7 => println!("Seven"),_ => println!("Other"),}}
}

簡潔控制流 if let

在數字 1-10 中隨機生成一個數,只有生成 6 才會顯示 “You win!”,用 match 的代碼如下:

use rand::Rng;
fn main() {let number = rand::thread_rng().gen_range(1..=10);println!("{}", number);match number {6 => println!("You win!"),_ => (),}
}

if let 語法以一種不那么冗長的方式結合 iflet,來處理只匹配一個模式的值而忽略其他模式的情況:

use rand::Rng;
fn main() {let number = rand::thread_rng().gen_range(1..=10);println!("{}", number);if let 6 = number {println!("You win!");};
}

if let 語法獲取通過等號分隔的一個模式和一個表達式。它的工作方式與 match 相同,這里的表達式對應 match 而模式則對應第一個分支。模式不匹配時 if let 塊中的代碼不會執行。

使用 if let 意味著編寫更少代碼,更少的縮進和更少的樣板代碼。然而,這樣會失去 match 強制要求的窮盡性檢查。matchif let 之間的選擇依賴特定的環境以及增加簡潔度和失去窮盡性檢查的權衡取舍。

換句話說,可以認為 if letmatch 的一個語法糖,它當值匹配某一模式時執行代碼而忽略所有其他值。

可以在 if let 中包含一個 elseelse 塊中的代碼與 match 表達式中的 _ 分支塊中的代碼相同,這樣的 match 表達式就等同于 if letelse

生成非 6 的數字顯示 “You lose!”:

use rand::Rng;
fn main() {let number = rand::thread_rng().gen_range(1..=10);println!("{}", number);if let 6 = number {println!("You win!");}else { println!("You lose!");}
}

if letelsematch 的簡化版,類似 ifelse,但專門匹配特定模式,更適合只關心一種匹配的情況。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/905206.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/905206.shtml
英文地址,請注明出處:http://en.pswp.cn/news/905206.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

應急響應靶機——WhereIS?

用戶名及密碼&#xff1a;zgsf/zgsf 下載資源還有個解題.exe: 1、攻擊者的兩個ip地址 2、flag1和flag2 3、后門程序進程名稱 4、攻擊者的提權方式(輸入程序名稱即可) 之前的命令&#xff1a; 1、攻擊者的兩個ip地址 先獲得root權限&#xff0c;查看一下歷史命令記錄&#x…

變量函數實戰:高保真APP原型“發票頁面”動態交互教程

變量函數是高保真交互原型設計中常見的高級交互功能&#xff0c;能夠避免重復復制與手動修改頁面元素和邏輯標注&#xff0c;讓演示更有真實體驗感。本文分享一個高保真APP交互原型頁面的實操案例&#xff0c;結合原型設計工具中的變量函數與邏輯判斷功能&#xff0c;手把手教你…

量子加密通信:守護信息安全的未來之盾

摘要 在數字化時代&#xff0c;信息安全成為全球關注的焦點。傳統加密技術面臨著被量子計算破解的風險&#xff0c;而量子加密通信作為一種基于量子力學原理的新型加密技術&#xff0c;提供了理論上無條件安全的通信保障。本文將詳細介紹量子加密通信的基本原理、技術實現、應用…

《Vue.js》閱讀之響應式數據與副作用函數

Vue.js 《Vue.js設計與實現》&#xff08;霍春陽&#xff09; 適合&#xff1a;從零手寫Vue3響應式系統&#xff0c;大廠面試源碼題直接覆蓋。重點章節&#xff1a;第4章&#xff08;響應式&#xff09;、第5章&#xff08;渲染器&#xff09;、第8章&#xff08;編譯器&…

數據處理專題(十三)

學會基本的圖像處理技術。? OpenCV 基礎 實踐&#xff1a;使用 OpenCV 進行圖像讀取、顯示和基本處理? 03 代碼示例 1. 導入必要的庫 import cv2import numpy as npimport matplotlib.pyplot as plt 2. 圖像讀取 # 讀取圖像image_path path_to_your_image.jpg # 替換…

springboot旅游小程序-計算機畢業設計源碼76696

目 錄 摘要 1 緒論 1.1研究背景與意義 1.2研究現狀 1.3論文結構與章節安排 2 基于微信小程序旅游網站系統分析 2.1 可行性分析 2.1.1 技術可行性分析 2.1.2 經濟可行性分析 2.1.3 法律可行性分析 2.2 系統功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系統…

P1874 快速求和

目錄 題目算法標簽: 動態規劃, 線性 d p dp dp思路代碼 題目 P1874 快速求和 算法標簽: 動態規劃, 線性 d p dp dp 思路 求的是最少組成 n n n的加法次數, 對于當前數字序列可以設計狀態表示 f [ i ] [ j ] f[i][j] f[i][j]表示考慮前 i i i個字符, 并且和是 j j j的所有方…

知名人工智能AI培訓公開課內訓課程培訓師培訓老師專家咨詢顧問唐興通AI在金融零售制造業醫藥服務業創新實踐應用

AI賦能未來工作&#xff1a;引爆效率與價值創造的實戰營 AI驅動的工作革命&#xff1a;從效率提升到價值共創 培訓時長&#xff1a; 本課程不僅是AI工具的操作指南&#xff0c;更是面向未來的工作方式升級羅盤。旨在幫助學員系統掌握AI&#xff08;特別是生成式AI/大語言模型…

Linux 內核參數

文章目錄 什么是內核參數參數種類配置方式1. 編譯內核時配置2. 內核啟動時配置3. 內核運行時配置4. 加載內核模塊時配置總結 什么是內核參數 內核參數是 Linux 系統中用于控制和調整內核行為的可配置選項。這些參數影響系統的性能、安全性和各種功能特性。 參數種類 大部分參…

pythonocc 拉伸特征

micromamba install -c conda-forge pythonocc-core opencascade.js安裝不起來&#xff0c;ai用pythonocc練個手 拉伸線框 線成面 from OCC.Core.gp import gp_Pnt, gp_Dir, gp_Vec from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeWire f…

Vue.js 頁面切換空白與刷新 404 問題深度解析

在使用 Vue.js 開發單頁應用 (SPA) 的過程中&#xff0c;開發者經常會遇到兩個常見問題&#xff1a;頁面切換時出現短暫的空白屏幕&#xff0c;以及刷新頁面時返回 404 錯誤。這兩個問題不僅影響用戶體驗&#xff0c;還可能阻礙項目的正常上線。本文將深入探討這兩個問題的成因…

Go 語言 slice(切片) 的使用

序言 在許多開發語言中&#xff0c;動態數組是必不可少的一個組成部分。在實際的開發中很少會使用到數組&#xff0c;因為對于數組的大小大多數情況下我們是不能事先就確定好的&#xff0c;所以他不夠靈活。動態數組通過提供自動擴容的機制&#xff0c;極大地提升了開發效率。這…

Qt5.14.2 鏈接 MySQL 8.4 遇到的問題

問題一: "Plugin caching_sha2_password could not be loaded: 找不到指定的模塊。 Library path is caching_sha2_password.dll QMYSQL: Unable to connect" 解決方法: alter user root@localhost identified with mysql_native_password by root;問題二: ERR…

Docker 部署 - Crawl4AI 文檔 (v0.5.x)

Docker 部署 - Crawl4AI 文檔 (v0.5.x) 快速入門 &#x1f680; 拉取并運行基礎版本&#xff1a; # 不帶安全性的基本運行 docker pull unclecode/crawl4ai:basic docker run -p 11235:11235 unclecode/crawl4ai:basic# 帶有 API 安全性啟用的運行 docker run -p 11235:1123…

開發工具分享: Web前端編碼常用的在線編譯器

1.OneCompiler 工具網址&#xff1a;https://onecompiler.com/ OneCompiler支持60多種編程語言&#xff0c;在全球有超過1280萬用戶&#xff0c;讓開發者可以輕易實現代碼的編寫、運行和共享。 OneCompiler的線上調試功能完全免費&#xff0c;對編程語言的覆蓋也很全&#x…

Docker-配置私有倉庫(Harbor)

配置私有倉庫&#xff08;Harbor&#xff09; 一、環境準備安裝 Docker 三、安裝docker-compose四、準備Harbor五、配置證書六、部署配置Harbor七、配置啟動服務八、定制本地倉庫九、測試本地倉庫 Harbor(港灣)&#xff0c;是一個用于 存儲 和 分發 Docker 鏡像的企業級 Regi…

關于高并發GIS數據處理的一點經驗分享

1、背景介紹 筆者過去幾年在參與某個大型央企的項目開發過程中,遇到了十分棘手的難題。其與我們平常接觸的項目性質完全不同。在一般的項目中,客戶一般只要求我們能夠通過桌面軟件對原始數據進行加工處理,將各類地理信息數據加工處理成地圖/場景和工作空間,然后再將工作空…

使用 DMM 測試 TDR

TDR&#xff08;時域反射計&#xff09;可能是實驗室中上升時間最快的儀器&#xff0c;但您可以使用直流歐姆表測試其準確性。 TDR 測量什么 在所有高速通道中&#xff0c;反射都很糟糕。我們嘗試設計一個通道來減少反射&#xff0c;這些反射都會導致符號間干擾 &#xff08;…

可視化圖解算法37:序列化二叉樹-II

1. 題目 描述 請實現兩個函數&#xff0c;分別用來序列化和反序列化二叉樹&#xff0c;不對序列化之后的字符串進行約束&#xff0c;但要求能夠根據序列化之后的字符串重新構造出一棵與原二叉樹相同的樹。 二叉樹的序列化(Serialize)是指&#xff1a;把一棵二叉樹按照某種遍…

【Python】Python常用數據類型詳解

Python常用數據類型詳解:增刪改查全掌握 Python作為一門簡潔高效的編程語言,其豐富的數據類型是構建程序的基礎。本文將詳細介紹數字、字符串、列表、元組、字典、集合這六種核心數據類型的特點及增刪改查操作,并附代碼示例,助你全面掌握數據操作技巧。 一、數字(Number)…