主頁 > 後端開發 > rust學習 - 構建mini 命令列工具

rust學習 - 構建mini 命令列工具

2023-06-09 07:49:00 後端開發

rust 的運行速度、安全性、單二進制檔案輸出和跨平臺支持使其成為構建命令列程式的最佳選擇,

實作一個命令列搜索工具grep,可以在指定檔案中搜索指定的字串,想實作這個功能呢,可以按照以下邏輯流程處理:

  1. 獲取輸入檔案路徑、需要搜索的字串
  2. 讀取檔案;
  3. 在檔案內容中查找字串所在的行
  4. 列印包含字串所在的行資訊

創建專案ifun-grep

$> cargo new ifun-grep

專案在運行時,可以獲取到傳遞的引數,比如cargo run -- hboot hello.txt,在檔案hello.txt查找字串hboot

讀取引數

首先要先獲取到傳入的引數,通過標準庫std::env::args獲取

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();

    dbg!(args);
}

collect()方法可以將傳入的引數轉換為一個集合,對于變數args必須注明集合型別,

args-print.jpg

引數的第一個值是二進制檔案的名稱,可以用于程式除錯或者列印出檔案路徑,取出另外兩個引數,保存進對應的變數,方便后續傳引數使用,

let search = &args[1];
let file_path = &args[2];

println!("will search {} in {}", search, file_path)

讀取檔案

首先創建測驗檔案hello.txt,并寫入一段文字,

獨立寒秋,湘江北去,橘子洲頭,

看萬山紅遍,層林盡染;漫江碧透,百舸爭流,

鷹擊長空,魚翔淺底,萬類霜天競自由,

悵寥廓,問蒼茫大地,誰主沉浮?

攜來百侶曾游,憶往昔崢嶸歲月稠,

恰同學少年,風華正茂;書生意氣,揮斥方遒,

指點江山,激揚文字,糞土當年萬戶侯,

曾記否,到中流擊水,浪遏飛舟

讀取檔案,并列印出檔案中的內容,

let content = fs::read_to_string(file_path).expect("you should permission to read the file");

println!("read the content:\n{content}")

通過fs模塊的read_to_string方法讀取檔案內容,expect則用于處理讀取檔案時發生的錯誤的提示資訊,這在下面的錯誤處理會有說明,

模塊拆分與錯誤處理

現在所有的處理業務都放在src/main.rs中,取參和讀取檔案是兩個不同功能的邏輯處理,當功能越來越復雜的時候,就應該關注分離,這在我們設計時可提前考慮好

main.rs只被用來處理程式的執行,其他需要處理的邏輯則可以放在srr/lib.rs中,

定義一個決議取參的函式parse_args,現在仍然定義在src/main.rs中,

fn parse_args(args: &Vec<String>) -> (&str, &str) {
    let search = &args[1];
    let file_path = &args[2];

    (search, file_path)
}

fn main(){
    let args: Vec<String> = env::args().collect();

    let (search, file_path) = parse_args(&args);

    println!("will search {} in {}", search, file_path);
}

這樣main函式不再處理哪個引數對應哪個變數,

我們可以將這一組相關的變數通過結構體定義相互關聯起來,這樣函式回傳將不再使用元組,并且可以通過結構體實體可以訪問到每一個屬性,

struct Config {
    search: String,
    file_path: String,
}

fn parse_args(args: &Vec<String>) -> Config {
    let search = args[1].clone();
    let file_path = args[2].clone();

    Config { search, file_path }
}

fn main(){
    let args: Vec<String> = env::args().collect();

    let config = parse_args(&args);

    println!("will search {} in {}", search, file_path);
}

在結構體中,實體化賦值需要擁有這些變數值的所有權,而變數args是所有權的擁有者,通過clone()方法拷貝一份資料,

可以看到parse_args回傳來一個結構體 Config 的實體,可以通過定義結構體的內部方法來創建實體,

impl Config {
    fn new(args: &Vec<String>) -> Self {
        let search = args[1].clone();
        let file_path = args[2].clone();

        Config { search, file_path }
    }
}

fn main(){
    let args: Vec<String> = env::args().collect();

    let config = Config::new(&args);

    println!("will search {} in {}", search, file_path);
}

這樣,就不需要parse_args函式了,通過結構體的內部方法實體化實體,

錯誤處理

如果我們執行cargo run時,不傳遞任何引數,則程式會報錯,這樣的提示對于用戶并不友好,

首先可以通過判斷引數需要的引數資訊,說明錯誤資訊,

impl Config {
    fn new(args: &Vec<String>) -> Self {
        if args.len() < 3 {
            panic!("至少傳入2個引數")
        }
        // ...
    }
}

提示用戶必須傳入 2 個從引數,因為有一個默認的路徑引數,所以判斷不能少于3

除了直接提示錯誤資訊并中斷程式,也可以使用Result傳遞錯誤,讓主函式做決定如何去處理,

impl Config {
    fn build(args: &Vec<String>) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("至少傳入2個引數");
        }
        let search = args[1].clone();
        let file_path = args[2].clone();

        Ok(Config { search, file_path })
    }
}

現在提供了一個方法build來處理這個邏輯,之前的new不用了(這里是語意話定義,new常常表示不會產生錯誤),當有錯誤時,不是直接終止程式,而是回傳一個Err值,

src/main.rs中呼叫并處理結果,對于錯誤資訊給用戶輸出有好的提示資訊,并以非零錯誤process::exit(1)退出命令列,

use std::{env, fs, process};

fn main(){
    let config = Config::build(&args).unwrap_or_else(|err| {
        println!("error occurred parseing args:{err}");
        process::exit(1);
    });

    // ...
}

unwrap_or_else可以進行自定義錯誤處理,這是一個閉包,它呼叫內部的匿名函式,并通過|err|傳遞的引數供內部使用,當回傳Ok時,則回傳內部的值,

提取讀取檔案的邏輯

引數的取參邏輯經由結構體內部方法處理,現在吧檔案讀取的邏輯提取出來,并采用傳遞錯誤的方式Result回傳錯誤資訊,

use std::error::Error;

fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let content = fs::read_to_string(config.file_path)?;

    println!("read the content:\n{content}");

    Ok(())
}

使用了 trait 物件 Box<dyn Error>回傳實作Errortrait 的型別,不用指定具體的錯誤型別,靈活性更高dyn表示動態的

接著可以在主函式中呼叫run()函式,并處理可能出現的錯誤,

fn main (){
    // ...

    if let Err(e) = run(config) {
        println!("something error:{e}");
        process::exit(1);
    }
}

拆分代碼到庫

以上定義了結構體,處理取參函式;拆離了讀取檔案邏輯,但是這些都是在src/main.rs中,有復雜邏輯時,這會讓檔案行數很多,看起來很讓人頭疼,

將這一部分拆離的放到其他檔案中去,新建src/lib.rs,將這些定義移動到該檔案中,

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

pub struct Config {
    pub search: String,
    pub file_path: String,
}

impl Config {
    pub fn build(args: &Vec<String>) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("至少傳入2個引數");
        }
        let search = args[1].clone();
        let file_path = args[2].clone();

        Ok(Config { search, file_path })
    }
}

pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let content = fs::read_to_string(config.file_path)?;

    println!("read the content:\n{content}");

    Ok(())
}

可以看到通過pub將這些結構體、函式都公有化,包括結構里的欄位,這就是一個可以測驗的公有 API 的 crate 庫,

然后再src/main.rs需要匯入

use ifun_grep::{run, Config};

通過use引入作用域,ifun-grep是專案名稱,作為前綴,

增加測驗

通過測驗驅動開發的模式來逐漸增加邏輯,期望從給定的內容中查找出字串,并列印出所在行,

src/lib.rs增加測驗示例

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn on_result() {
        let search = "hboot";
        let content = "\
nice. rust
I'm hboot.
hello world.
";

        assert_eq!(vec!["I'm hboot."], find(search, content));
    }
}

搜索字串hboot,它在文本的第二行,所以期待搜索輸出結果為I'm hboot.

提供一個find函式,用于處理搜索邏輯,先不寫搜索邏輯,回傳一個空的結果值,

pub fn find<'a>(search: &str, content: &'a str) -> Vec<&'a str> {
    vec![]
}

利用顯示生命周期'a來表明引數content引數與回傳值的生命周期相關聯,它們存在的時間一樣久

執行測驗cargo test,理所應當的輸出失敗,結果回傳了一個空的vec![],和預期不匹配,

增加搜索邏輯,按行執行過濾,包含指定的字串,則存盤在結果中,

pub fn find<'a>(search: &str, content: &'a str) -> Vec<&'a str> {
    let mut result = vec![];
    for line in content.lines() {
        if line.contains(search) {
            // 符合,包含了指定字串
            result.push(line);
        }
    }

    result
}

通過迭代器遍歷給定文本內容lines().字串判斷是否包含contains()方法,將結果值放進result中,并回傳,

測驗用例測驗沒有問題,完善一下run函式,搜索出符合的內容并列印出來

pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let content = fs::read_to_string(config.file_path)?;

    // println!("read the content:\n{content}");
    for line in find(&config.search, &content) {
        println!("{line}");
    }

    Ok(())
}

執行腳本cargo run -- 山 hello.txt,可以看到列印輸出兩行

run-success.png

增加環境變數

功能已經到到預期,可以搜索出想要包含字串的文本段落,增加一個額外的功能大小寫敏感處理環境變數,當然也可以通過再多傳一個引數處理,

更改文本內容為應為

Let life be beautiful like summer flowers.

The world has kissed my soul with its pain.

Eyes are raining for her.

you also miss the stars.

先測驗當前程式是否大小寫敏感,文本中首個英文單詞是大寫的,按照小寫搜索

$> cargo run -- let hello.txt

沒有任何的列印輸出,說明當前的搜索邏輯是大小寫敏感的,通過傳遞變數來控制邏輯,修改測驗用例,增加兩個測驗示例:大小寫敏感和不敏感測驗,

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn case_sensitive() {
        let search = "rust";
        let content = "\
nice. rust
I'm hboot.
hello world.
Rust
";

        assert_eq!(vec!["nice. rust"], find(search, content));
    }

    #[test]
    fn case_insensitive() {
        let search = "rust";
        let content = "\
nice. rust
I'm hboot.
hello world.
Rust
";

        assert_eq!(vec!["nice. rust", "Rust"], find_insensitive(search, content));
    }
}

原來的函式find大小寫敏感,邏輯不變,增加一個大小寫不敏感的函式find_insensitive,在處理搜索時,查詢的字符和被搜索的文本行都轉小寫后,然后在執行查找,

pub fn find_insensitive<'a>(search: &str, content: &'a str) -> Vec<&'a str> {
    let mut result = vec![];
    // 搜索 字串轉小寫
    let search = search.to_lowercase();

    for line in content.lines() {
        // 文本行內容轉小寫
        if line.to_lowercase().contains(&search) {
            // 符合,包含了指定字串
            result.push(line);
        }
    }

    result
}

多了一個操作to_lowercase()將文本內容轉成小寫,to_lowercase()會新創建一個 String,contains()方法引數需要的是一個參考,

再次執行測驗cargo teset.用例全部通過,邏輯寫好了,需要通過增加一個配置來處理是否大小寫敏感,

修改結構體定義ingore_case表示來忽略大小寫,

pub struct Config {
    pub search: String,
    pub file_path: String,
    pub ignore_case: bool,
}

通過ingore_case欄位判斷是否呼叫哪個函式,修改run函式

pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let content = fs::read_to_string(config.file_path)?;

    // println!("read the content:\n{content}");
    let mut result = vec![];
    if config.ignore_case {
        result = find_insensitive(&config.search, &content)
    } else {
        result = find(&config.search, &content)
    }
    for line in result {
        println!("{line}");
    }

    Ok(())
}

處理接受變數IGNORE_CASE,通過庫std::env處理環境變數,

impl Config {
    pub fn build(args: &Vec<String>) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("至少傳入2個引數");
        }
        let search = args[1].clone();
        let file_path = args[2].clone();

        let ignore_case = env::var("IGNORE_CASE").is_ok();

        Ok(Config {
            search,
            file_path,
            ignore_case,
        })
    }
}

env::var()回傳值為 Result 型別,通過它自己的方法is_ok()判斷什么狀態,如果設定值則回傳 true;未設定則回傳 false,

進行測驗,不設定變數時,查詢小寫的let是查詢不到的,因為首寫的因為單詞字母是大些的,

$> cargo run -- let hello.txt

test-case-sensitive.png

通過設定環境變數,執行程式

$> IGNORE_CASE=1 cargo run -- let hello.txt

可以查到目標文本內容,

teset-case-insensitive.png

錯誤資訊處理

我們所預先知道的錯誤資訊都通程序式執行println!列印在控制臺,這是一種標準輸出.

對于出現錯誤資訊,希望它即時列印輸出,而對于程式執行的結果記錄下來,保存到檔案中,方便查看,

現在使用println!標準輸出流重定向到檔案中,它會將錯誤資訊也保存到起來,且不會列印,

$> cargo run >output.txt

螢屏上沒有任何輸出,以為程式執行正常,其實檔案中的內容是error occurred parseing args:至少傳入2個引數

這就造成了一個問題,不管成功、失敗,只有打開檔案才能看到,錯誤輸出使用標準錯誤展示用于錯誤資訊,將錯誤列印的println!改為eprintln!

fn main() {
    let args: Vec<String> = env::args().collect();

    let config = Config::build(&args).unwrap_or_else(|err| {
        // println!("error occurred parseing args:{err}");
        eprintln!("error occurred parseing args:{err}");
        process::exit(1);
    });
    println!("will search {} in {}", config.search, config.file_path);

    if let Err(e) = run(config) {
        // println!("something error:{e}");
        eprintln!("something error:{e}");
        process::exit(1);
    }
}

重新執行cargo run >output.txt,錯誤列印到控制臺,而檔案output.txt沒有輸出,

再執行,可以查到資料的命令cargo run -- Let hello.txt > output.txt,查看output.txt,可以看到預期的查找到的內容在檔案中,

發布 crate 到Crate.io

crates.io 庫,可以這里找找想要的功能庫,也可以將自己的 crate 發布到這里,

Rust 的發布配置都有一套默認的、可定制的配置,

  • cargo build 采用的是 dev 配置構建程式
  • cargo build --release 是 release 配置,有更好的發布構建的配置

可以在檔案Cargo.toml中通過[profile.*]修改設定默認值,

[profile.dev]
opt-level = 0

[profile.release]
opt-level = 3

dev 構建和發布構建定義不同的優化等級,opt-level定義何種程度優化,0-3可配置值,dev 默認為 0,release 默認為 3.

如果想 dev 模式下需要一些優化,則可以更改為

[profile.dev]
opt-level = 1

增加檔案注釋

一個好的模塊包,是有很好的檔案說明,以方便其他人輕易上手,通過檔案注釋///已支持 markdown 格式化文本,

給每一個函式增加注釋說明,這里只展示部分,

/// the struct `Config` defines command line params.
///
/// # Example
///
/// ```
/// let search = String::from("let");
/// let config = ifun_grep::Config {
///     search,
///     file_path:String::from("hello.txt"),
///     ignore_case:false,
/// };
///
/// ```
pub struct Config {
    pub search: String,
    pub file_path: String,
    pub ignore_case: bool,
}

/// the fun is used to execute search
///
/// # example
/// ```
/// let search = String::from("let");
/// let config = ifun_grep::Config {
///     search,
///     file_path:String::from("hello.txt"),
///     ignore_case:false,
/// };
///
/// let result = ifun_grep::run(config);
///
/// assert!(result.is_ok());
/// ```
///
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let content = fs::read_to_string(config.file_path)?;

    // println!("read the content:\n{content}");
    let result;
    if config.ignore_case {
        result = find_insensitive(&config.search, &content);
    } else {
        result = find(&config.search, &content);
    }
    for line in result {
        println!("{line}");
    }

    Ok(())
}

在使用 vscode 時,注釋檔案上方會有一個執行操作 run doctest,可以單獨執行當前寫的測驗示例是否可以通過執行,

也可以通過cargo test來測驗所有的測驗示例,不僅會執行mod test的測驗示例,也會執行doc test的注釋測驗示例,

通過命令cargo doc --open來生成在線檔案,

$> cargo doc --open

crate-doc.png

可以通過//!對當前檔案進行注釋說明,必須是在第一行,

//! ifun_grep is a string search library
//!
//! Supports case sensitive search.
//!

注冊 crate.io 賬戶并發布

目前只能使用 github 賬號進行授權登錄,在個人賬號資訊中,API Tokens生成 token 授權操作,

$> cargo login 你的token

如果登錄不成功,看下提示錯誤,我是加了引數--registry crates-io才成功的,

$> cargo login 你的token --registry crates-io

登錄之后就可以發布了,通過Cargo.toml增加一些倉庫元資訊,比如倉庫名、作者、開源協議、描述等等,

$> cargo publish

發布之前需要驗證你登錄的賬號郵箱,不然發布不了,個人的元資訊有幾項是必填的,包括name\version\description\license

發布時,如果發布不成功,看錯誤提示,可能還需要加--registry crates-io

撤銷某個版本

如果你發布的版本有很大的問題,可以撤銷改版本,不能洗掉倉庫,已發布的代碼時永久存在的,只能通過撤銷來阻止其他專案參考它,

$> cargo yank --vers 0.1.0

使得當前版本不可用,也可以恢復當前版本的使用

$> cargo yank --vers 0.1.0 --undo
追逐的不應該是夢想,隨心所欲,隨遇而安!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/554674.html

標籤:其他

上一篇:如何吃透一個Java專案?

下一篇:返回列表

標籤雲
其他(160616) Python(38218) JavaScript(25485) Java(18210) C(15237) 區塊鏈(8270) C#(7972) AI(7469) 爪哇(7425) MySQL(7238) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5873) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4588) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2435) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1984) 功能(1967) HtmlCss(1956) Web開發(1951) C++(1933) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1880) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • rust學習 - 構建mini 命令列工具

    rust 的運行速度、安全性、單二進制檔案輸出和跨平臺支持使其成為構建命令列程式的最佳選擇。 實作一個命令列搜索工具`grep`,可以在指定檔案中搜索指定的字串。想實作這個功能呢,可以按照以下邏輯流程處理: 1. 獲取輸入檔案路徑、需要搜索的字串 2. 讀取檔案; 3. 在檔案內容中查找字串所 ......

    uj5u.com 2023-06-09 07:49:00 more
  • 如何吃透一個Java專案?

    現在Austin的檔案我覺得還是比較全的,但到了看代碼的時候,可能有的同學就不知道應該怎么看,有想知道模塊之間的呼叫鏈路,有想一點一點把細節給全看了。這時候就很可能在專案里犯迷糊了,繞不出不來了。 > **Java開源專案訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信 ......

    uj5u.com 2023-06-09 07:48:55 more
  • 物體類中嵌套Enum型別并想轉換成JSON字串時遇到的問題。

    ## 物體類中嵌套Enum型別并想轉換成JSON字串時遇到的問題。 先說明問題的產生,在自己寫著玩的時候,新建了一個**User**類如下: ```java package com.ma.xdo; import lombok.*; import java.io.Serializable; /** ......

    uj5u.com 2023-06-09 07:48:50 more
  • Rust語言 - 介面設計的建議之不意外(unsurprising)

    # Rust - 介面設計建議之不意外(unsurprising) 書:Rust for Rustaceans ## Rust介面設計的原則(建議) - 四個原則: - 不意外(unsurprising) - 靈活(flexible) - 顯而易見(obvious) - 受約束(constraine ......

    uj5u.com 2023-06-09 07:48:46 more
  • Mybatis的parameterType造成執行緒阻塞問題分析

    最近在新發布某個專案上線時,每次重啟都會收到機器的 CPU 使用率告警,查看對應監控,持續時長達 5 分鐘,對于服務重啟有很大風險。而該專案有非常多 Consumer 消費,服務啟動后會有大量執行緒去拉取訊息處理邏輯,通過多次 Jstack 輸出執行緒快照發現有很多 BLOCKED 狀態執行緒,此文主要記... ......

    uj5u.com 2023-06-09 07:48:41 more
  • 【python基礎】回圈陳述句-continue關鍵字

    # 1.continue關鍵字 continue關鍵字的作用是:用來告訴 Python 跳過當前回圈代碼塊中的剩余陳述句,然后繼續進行下一輪回圈。 其在while回圈和for回圈中的作用示意圖如下 ![image](https://img2023.cnblogs.com/blog/3179433/20 ......

    uj5u.com 2023-06-09 07:43:23 more
  • static

    | static基本知識 | header | | | | | | | 類名.靜態成員變數(推薦) 同一個類中靜態成員變數的訪問可以省略類名。 1.靜態成員變數(有static修飾,屬于類、加載一次,可以被共享訪問),訪問格式 類名.靜態成員變數(推薦) 物件.靜態成員變數(不推薦)。 2.實體成員 ......

    uj5u.com 2023-06-09 07:21:44 more
  • java~如何使用無符號整型

    在 Java 中,沒有直接支持無符號整數的資料型別。Java 的基本資料型別(如 int、long、short、byte)都是帶符號的,即它們可以表示正數和負數。 > .net中每種整型都有對應的無符號型別,它不會把取值范圍分成正負兩個區間,只在正整數范圍內取值 然而,你可以使用 Java 中的較大 ......

    uj5u.com 2023-06-08 11:55:03 more
  • 【技識訓累】Python中的NumPy庫【一】

    博客推行版本更新,成果積累制度,已經寫過的博客還會再次更新,不斷地琢磨,高質量高數量都是要追求的,工匠精神是學習必不可少的精神。因此,大家有何建議歡迎在評論區踴躍發言,你們的支持是我最大的動力,你們敢投,我就敢肝 ......

    uj5u.com 2023-06-08 11:54:25 more
  • 【python基礎】回圈陳述句-break關鍵字

    # 1.break關鍵字 break關鍵字,其作用是在回圈中的代碼塊遇到此關鍵字,立刻跳出整個回圈,執行回圈外的下一條陳述句。 其在while和for回圈中的作用示意圖如下: ![image](https://img2023.cnblogs.com/blog/3179433/202306/317943 ......

    uj5u.com 2023-06-08 11:26:29 more