rust学习之注释及格式化输出

注释种类

注释分为代码注释、文档注释、包和模块注释。代码注释方式有行注释//,块注释/*   */两种。

文档注释需要位于 lib 类型的包中,例如 src/lib.rs 中。可以使用 markdown语法。被注释的对象需要使用 pub 对外可见。Rust 提供了cargo doc --open 命令,可以用于把这些文档注释转换成 HTML 网页文件。文档行注释:/// ,文档块注释:/**  */

对包和模块的注释要添加到包、模块的最上方,包级别的注释也分为两种:行注释 //! 和块注释 /*! ... */。Rust 允许我们在文档注释中写单元测试用例!注释不仅仅是文档,还可以作为单元测试的用例运行,使用 cargo test 运行测试

文档测试中的用例还可以造成 panic

#![allow(unused)]
fn main() {
/// # Panics
///
/// The function panics if the second argument is zero.
///
/// ```rust
/// // panics on division by zero
/// world_hello::compute::div(10, 0);
/// ```
pub fn div(a: i32, b: i32) -> i32 {
    if b == 0 {
        panic!("Divide-by-zero error");
    }

    a / b
}
}

以上测试运行后会 panic如果想要通过这种测试,可以添加 should_panic

/// # Panics
///
/// The function panics if the second argument is zero.
///
/// ```rust,should_panic
/// // panics on division by zero
/// world_hello::compute::div(10, 0);
/// ```

我们使用 # 将不想让用户看到的内容隐藏起来,但是又不影响测试用例的运行,

/// ```
/// # // 使用#开头的行会在文档中被隐藏起来,但是依然会在文档测试中运行
/// # fn try_main() -> Result<(), String> {
/// let res = world_hello::compute::try_div(10, 0)?;
/// # Ok(()) // returning from try_main
/// # }
/// # fn main() {
/// #    try_main().unwrap();
/// #
/// # }
/// ```
pub fn try_div(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err(String::from("Divide-by-zero"))
    } else {
        Ok(a / b)
    }
}

最终用户将只能看到那行没有隐藏的 let res = world_hello::compute::try_div(10, 0)?;

一个带包文档的综合例子:

首先,使用 cargo new art 创建一个 Package art:该命令创建了一个二进制 Package,它包含一个同名的二进制包:包名为 art,包根为 src/main.rs src 目录下再创建一个 lib.rs 文件,等同于又创建了一个库类型的包,包名也是 art,包根为 src/lib.rs将以下内容添加到 src/lib.rs 中:

//! # Art
//!
//!  未来的艺术建模库,现在的调色库

pub use self::kinds::PrimaryColor;
pub use self::kinds::SecondaryColor;
pub use self::utils::mix;

pub mod kinds {
    //! 定义颜色的类型

    /// 主色
    pub enum PrimaryColor {
        Red,
        Yellow,
        Blue,
    }

    /// 副色
    #[derive(Debug,PartialEq)]
    pub enum SecondaryColor {
        Orange,
        Green,
        Purple,
    }
}

pub mod utils {
    //! 实用工具,目前只实现了调色板
    use crate::kinds::*;

    /// 将两种主色调成副色
    /// ```rust
    /// use art::utils::mix;
    /// use art::kinds::{PrimaryColor,SecondaryColor};
    /// assert!(matches!(mix(PrimaryColor::Yellow, PrimaryColor::Blue), SecondaryColor::Green));
    /// ```
    pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
        SecondaryColor::Green
    }
}

接着,将下面内容添加到 src/main.rs 中:

use art::kinds::PrimaryColor;
use art::utils::mix;

fn main() {
    let blue = PrimaryColor::Blue;
    let yellow = PrimaryColor::Yellow;
    println!("{:?}",mix(blue, yellow));
}

使用 cargo run 运行,输出Green。执行cargo doc --open 命令,可以在生成文档如下:

 格式化输出

Rust 中用来格式化输出的三大金刚,用途如下:

  • print! 将格式化文本输出到标准输出,不带换行符
  • println! 同上,但是在行的末尾添加换行符
  • format! 将格式化文本输出到 String 字符串

还有两大护法,使用方式跟 print!println! 很像,但是它们输出到标准错误输出:

eprintln!("Error: Could not complete task");它们仅应该被用于输出错误信息和进度信息

Rust 统一用{} 作为格式化占位符 ,{:?} 也是占位符:

  • {} 适用于实现了 std::fmt::Display 特征的类型,用来以更优雅、更友好的方式格式化文本,例如展示给用户
  • {:?} 适用于实现了 std::fmt::Debug 特征的类型,用于调试场景

 大多数 Rust 类型都实现了 Debug 特征或者支持派生该特征,对于数值、字符串、数组,可以直接使用 {:?} 进行输出,但是对于结构体,需要派生Debug特征后,才能进行输出。而实现了 Display 特征的 Rust 类型并没有那么多,往往需要我们自定义想要的格式化方式,对于没有实现 Display 特征的,处理方式一般有三种:

  • 使用 {:?} 或 {:#?}
  • 为自定义类型实现 Display 特征
  • 使用 newtype 为外部类型实现 Display 特征

其中,{:#?} 与 {:?} 几乎一样,唯一的区别在于它能更优美地输出内容。如果类型是定义在当前作用域中的,那么可以为其实现 Display 特征 :

struct Person {
    name: String,
    age: u8,
}

use std::fmt;
impl fmt::Display for Person {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "大佬在上,请受我一拜,小弟姓名{},年芳{},家里无田又无车,生活苦哈哈",
            self.name, self.age
        )
    }
}
fn main() {
    let p = Person {
        name: "sunface".to_string(),
        age: 18,
    };
    println!("{}", p);
}
输出如下:
大佬在上,请受我一拜,小弟姓名sunface,年芳18,家里无田又无车,生活苦哈哈
在 Rust 中,无法直接为外部类型实现外部特征,但可以使用newtype解决此问题:
struct Array(Vec<i32>);

use std::fmt;
impl fmt::Display for Array {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "数组是:{:?}", self.0)
    }
}
fn main() {
    let arr = Array(vec![1, 2, 3]);
    println!("{}", arr);
}
输出结果
数组是:[1, 2, 3]

Array 就是我们的 newtype,它将想要格式化输出的 Vec 包裹在内,最后只要为 Array 实现 Display 特征,即可进行格式化输出

格式化输出中的一些参数

除了默认按照依次顺序使用值去替换占位符之外(依次填充占位符),还能让指定位置的参数去替换某个占位符,例如 {1},表示用第二个参数替换该占位符(索引从 0 开始) 。我们还可以为参数指定名称,让对应名称的值填充占位符,但带名称的参数必须放在不带名称参数的后面否则会报错

println!("{abc} {1}", abc = "def", 2); //编译报错,应该写成println!("{abc} {0}", 2,abc = "def");

格式化输出还可以指定输出小数点后几位, 字符串格式化默认使用空格进行填充,并且进行左对齐。数字格式化默认也是使用空格进行填充,但是右对齐。可以使用 # 号来控制数字的进制输出:

fn main() {
    //----------------字符串填充-------------------
    // 以下全部输出 "Hello x    !"
    // 为"x"后面填充空格,补齐宽度5
    println!("Hello {:5}!", "x");
    // 使用参数5来指定宽度
    println!("Hello {:1$}!", "x", 5);
    // 使用x作为占位符输出内容,同时使用5作为宽度
    println!("Hello {1:0$}!", 5, "x");
    // 使用有名称的参数作为宽度
    println!("Hello {:width$}!", "x", width = 5);
    //-----------------------------------

    // 使用参数5为参数x指定宽度,同时在结尾输出参数5 => Hello x    !5
    println!("Hello {:1$}!{}", "x", 5);
    //----------------数字填充-------------------
    // 宽度是5 => Hello     5!
    println!("Hello {:5}!", 5);
    // 显式的输出正号 => Hello +5!
    println!("Hello {:+}!", 5);
    // 宽度5,使用0进行填充 => Hello 00005!
    println!("Hello {:05}!", 5);
    // 负号也要占用一位宽度 => Hello -0005!
    println!("Hello {:05}!", -5);
    //----------------对齐-------------------
    // 以下全部都会补齐5个字符的长度
    // 左对齐 => Hello x    !
    println!("Hello {:<5}!", "x");
    // 右对齐 => Hello     x!
    println!("Hello {:>5}!", "x");
    // 居中对齐 => Hello   x  !
    println!("Hello {:^5}!", "x");

    // 对齐并使用指定符号填充 => Hello x&&&&!
    // 指定符号填充的前提条件是必须有对齐字符
    println!("Hello {:&<5}!", "x");
    //----------------精度-------------------
     let v = 3.1415926;
    // 保留小数点后两位 => 3.14
    println!("{:.2}", v);
    // 带符号保留小数点后两位 => +3.14
    println!("{:+.2}", v);
    // 不带小数 => 3
    println!("{:.0}", v);
    // 通过参数来设定精度 => 3.1416,相当于{:.4}
    println!("{:.1$}", v, 4);

    let s = "hi我是Sunface孙飞";
    // 保留字符串前三个字符 => hi我
    println!("{:.3}", s);
    // {:.*}接收两个参数,第一个是精度,第二个是被格式化的值 => Hello abc!
    println!("Hello {:.*}!", 3, "abcdefg");
    //----------------进制-------------------
    // 二进制 => 0b11011!
    println!("{:#b}!", 27);
    // 八进制 => 0o33!
    println!("{:#o}!", 27);
    // 十进制 => 27!
    println!("{}!", 27);
    // 小写十六进制 => 0x1b!
    println!("{:#x}!", 27);
    // 大写十六进制 => 0x1B!
    println!("{:#X}!", 27);

    // 不带前缀的十六进制 => 1b!
    println!("{:x}!", 27);

    // 使用0填充二进制,宽度为10 => 0b00011011!
    println!("{:#010b}!", 27);
}

指数及指针地址的打印,有时确实要输出{}时,就需要使用转义:

fn main() {
    println!("{:2e}", 1000000000); // => 1e9
    println!("{:2E}", 1000000000); // => 1E9
    let v= vec![1, 2, 3];
    println!("{:p}", v.as_ptr()) // => 0x600002324050
    // "{{" 转义为 '{'   "}}" 转义为 '}'   "\"" 转义为 '"'
    // => Hello "{World}" 
    println!(" Hello \"{{World}}\" ");
    // 不可使用 '\' 来转义 "{}"
    // println!(" \{ Hello \} ")
}
println!("Hello");                 // => "Hello"
println!("Hello, {}!", "world");   // => "Hello, world!"
println!("The number is {}", 1);   // => "The number is 1"
println!("{:?}", (3, 4));          // => "(3, 4)"
println!("{value}", value=4);      // => "4"
println!("{} {}", 1, 2);           // => "1 2"
println!("{:04}", 42);             // => "0042" with leading zeros

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值