Rust-格式化输出

format

  1. 打印操作由 std::fmt 里面所定义的一系列来处理
    • 并不产生函数调用,而是展开成源码,并和程序的其余部分一起被编译。Rust 又有一点和 C 以及其他语言都不同,那就是 Rust 的宏会展开为抽象语法树(AST,abstract syntax tree),而不是像字符串预处理那样直接替换成代码,这样就不会产生无法预料的优先权错误

    • 宏是通过 macro_rules! 宏来创建的

      // 这是一个简单的宏,名为 `say_hello`。
      macro_rules! say_hello {
          // `()` 表示此宏不接受任何参数。
          () => (
              // 此宏将会展开成这个代码块里面的内容。
              println!("Hello!");
          )
      }
      
      fn main() {
          // 这个调用将会展开成 `println("Hello");`!
          say_hello!()
      }
      
    • 宏的作用

      • 不写重复代码(DRY,Don’t repeat yourself.)
        • 很多时候你需要在一些地方针对不同的类型实现类似的功能,这时常常可以使用宏来避免重复代码
      • 领域专用语言(DSL,domain-specific language)
        • 宏允许你为特定的目的创造特定的语法
      • 可变接口(variadic interface)
        • 有时你需要能够接受不定数目参数的接口,比如 println!,根据格式化字符串的不同,它需要接受任意多的参数
  2. std::fmt包含多种 traits(特征,特性) 来控制文字显示,其中重要的两种 trait 的基本形式如下
    • fmt::Debug:使用 {:?} 标记。格式化文本以供调试使用
    • fmt::Display:使用 {} 标记。以更优雅和友好的风格来格式化文本
  3. 标准库提供了基本类型的fmt::Display的实现,如果需要打印自定义结构体,需要更多步骤
    • 标准库提供
      • format!:将格式化文本写到字符串(String)。(注:字符串是返回值不是参数)
      • print!:与 format! 类似,但将文本输出到控制台(io::stdout
      • eprint!:与 format! 类似,但将文本输出到标准错误(io::stderr
    • 自定义结构体
      • println!("This struct:{}", Structure(3));是错误的
  4. 格式化输出
    • {0:>04b}{0:>.05b}
      • 0:第1个参数
      • ::指示后面跟特殊格式
      • 0:用零补全
      • 4:整数占据4位
      • .5:小数占据5位
      • <^>:指示左、中、右对齐
      • xob:指示16、8、2进制
    • {0}:指示第0个参数
    • {name}:命名参数
    • 精度
      • 整数(忽略精度)

      • 浮点数

        • .5:小数精度为5位
        • .1$:需要格式化输入参数中有一个usize作为精度
          • 1:表示第2个参数(打印内容)
          • $:通配符(usize)
        • .*:需要格式化输入参数中有usize打印内容
          • 打印内容
          • usize
        let pi = 3.1415926;
        println!(".N 表示精度:Pi is roughtly {0:>.3}",pi);         //.N表示精度
        println!(".N$ 表示精度:Pi is {:.1$}", pi, 5);      //.N$表示精度
        println!(".* 表示精度:Pi is {:.*}", 6, pi);        //.*表示精度
        println!("Pi is roughtly {:<04}, 0.5 is {:^04} of {:>08b}", pi, 1, 2);   //:04b 占4位其余补零,b指示为二进制
        

fmt::Debug

  1. 所有类型都能推导(derive,即自动创建)fmt::Debug的实现,但是,fmt::Display 需要手动实现

    // 推导 `Structure` 的 `fmt::Debug` 实现
    //否则显示 Structure` cannot be formatted using `{:?}`
    #[derive(Debug)]
    struct Structure(i32);  //包含单个 `i32` 的结构体。
    
    #[derive(Debug)]    //使 `Deep` 也能够打印
    struct Deep(Structure); //将 `Structure` 放到结构体 `Deep` 中
    
    fn main(){
        // `Structure` 也可以打印!(#[derive(Debug)])
        println!("Now {:?} will print!", Structure(3));
        // 使用 `derive` 的一个问题是不能控制输出的形式,如只想展示一个`7`该怎么实现?=> fmt::Display
        println!("Now {:?} will print!", Deep(Structure(7)));
    }
    
  2. debug的美化打印(待理解'a

    #[derive(Debug)]
    struct Person<'a> {
        name: &'a str,
        age: u8
    }
    
    fn main() {
        let name = "Peter";
        let age = 27;
        let peter = Person { name, age };
    
        println!("{:#?}", peter);   // 美化打印
    }
    //输出
    //Person {
    //    name: "Peter",
    //    age: 27,
    //}
    

fmt::Display

  1. 为自定义结构手动实现fmt::Display

    • 单个元素

      // (使用 `use`)导入 `fmt` 模块使 `fmt::Display` 可用
      use std::fmt;
      
      // 定义一个包含单个 `i32` 元素的结构体
      struct Structure(i32);
      
      // 为了使用 `{}` 标记,必须手动为类型实现 `fmt::Display` 的 trait
      impl fmt::Display for Structure {   //implement 实现
          // 这个 trait 要求 `fmt` 使用与下面的函数完全一致的函数签名
          fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
              // 仅将 self 的第一个元素写入到给定的输出流 `f`。返回 `fmt:Result`,此
              // 结果表明操作成功或失败。注意 `write!` 的用法和 `println!` 很相似。
              write!(f, "{}", self.0)
          }
      }
      
    • 多个元素

      • 带有两个数字的结构体

        //推导出 `Debug`,以便与 `Display` 的输出进行比较
        #[derive(Debug)]
        struct MinMax(i64, i64);
        
        // 实现 `MinMax` 的 `Display`。
        impl fmt::Display for MinMax {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                // 使用 `self.number` 来表示各个数据。
                write!(f, "({}, {})", self.0, self.1)
            }
        }    
        
      • 定义一个含有具名字段的结构体

        #[derive(Debug)]
        struct Point2D {
            x: f64,
            y: f64,
        }
        
        // 类似地对 `Point2D` 实现 `Display`
        impl fmt::Display for Point2D {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                // 自定义格式,使得仅显示 `x` 和 `y` 的值。
                write!(f, "x: {}, y: {}", self.x, self.y)
            }
        }
        
      • 运行测试

        fn main() {
            //Debug
            println!("Debug print {:?}", Structure(3));     //Debug打印自定义结构体
            // 使用 `derive` 的一个问题是不能控制输出的形式,如只想展示一个`7`该怎么实现?
            println!("Debug print {:?}", Deep(Structure(7)));            
        
            println!("比较Debug和Display:");
            let big_range = MinMax(99,999);
            let small_range = MinMax(1,9);
            let point = Point2D{x: 3.3, y: 6.6};
            //Debug
            println!("Debug:print {:?} and {:?}",big_range, small_range);
            println!("Debug:print {:?}", point);
            //Display
            println!("print {big} and {small}", big = big_range, small = small_range);
            println!("print {}", point);
            
            // 报错,`Debug` 和 `Display` 都被实现了,但 `{:b}` 还需要 `fmt::Binary`得到实现
            // println!("What does Point2D look like in binary: {:b}?", point);
        }
        
  2. 实现复数Complex结构的的fmt::Debug和fmt::Display

    #[derive(Debug)]    //为Complex实现fmt::Debug
    struct Complex{ //定义复数结构(re+im*i)
        re: f64,
        im: f64,
    }
    
    impl fmt::Display for Complex{  //为Complex实现Display
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
            write!(f, "{} + {}i", self.re, self.im)
        }
    }
    
    fn main(){
        let complex = Complex{re: 1.2, im: 3.4};
        println!("Debug print Complex:{:?}",complex);
        println!("Display print Complex:{}",complex);
    }
    
  3. trait是对未知类型 Self 定义的方法集,该类型也可以访问同一个 trait 中定义的其他方法

List的fmt::Debug和fmt::Displays实现

  1. List结构

    #[derive(Debug)]        //为List实现fmt::Debug
    struct List(Vec<i32>);  //定义包含单个 `Vec` 的结构体 `List`
    
  2. 实现List结构的的fmt::Debug和fmt::Display

    impl fmt::Display for List{ //为List实现fmt::Display
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
            let vec = &self.0;
            write!(f,"[")?; //向输出流f中写入`[`
            for(count, v) in vec.iter().enumerate(){
                if count != 0 { write!(f, ",")?;}   //除第一个元素外都加上逗号`,`
                write!(f, "{}: {}", count, v)?;    //向输出流f中写入值
            }
            // 加上配对中括号,并返回一个 fmt::Result 值。
            write!(f, "]")
        }
    }
    

City结构体的fmt::Debug和fmt::Displays实现

  1. City结构

    #[derive(Debug)]
    struct City{
        name: &'static str, //城市名称
        lat: f32,   //纬度
        lon: f32,   //经度
    }
    
  2. 实现City结构的的fmt::Debug和fmt::Display

    impl fmt::Display for City{
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
            let lat_c = if self.lat >= 0.0 { 'N' } else { 'S' };
            let lon_c = if self.lon >= 0.0 { 'E' } else { 'W' };
            //write!和format类似,将格式化字符串写入缓冲区 f
            write!(f, "{}: {}°{}, {}°{}", self.name,
                self.lat, lat_c, self.lon, lon_c)
        }
    }
    
  3. 测试输出

    fn main(){    
        let city = City{ name: "HangZhou", lat: 30.0, lon: 120.0 };
        println!("Debug City print:{:?}", city);
        println!("Display City print:{}", city);
    
        println!("----------loop_city----------");
        for city in [
            City{ name: "Dublin", lat: 53.347778, lon: -6.259772 },
            City{ name: "Oslo", lat: 59.95, lon: 10.75 },
            City{ name: "Vancouver", lat: 49.25, lon: -123.1 },
        ].iter() {
            println!("Debug City print:{:?}", *city);
            println!("Display City print:{}", *city);
        }
    }
    //输出结果
    //----------loop_city----------
    //Debug City print:City { name: "Dublin", lat: 53.34778, lon: -6.259772 }
    //Display City print:Dublin: 53.34778°N, -6.259772°W
    //Debug City print:City { name: "Oslo", lat: 59.95, lon: 10.75 }
    //Display City print:Oslo: 59.95°N, 10.75°E
    //Debug City print:City { name: "Vancouver", lat: 49.25, lon: -123.1 }
    //Display City print:Vancouver: 49.25°N, -123.1°W
    

Color结构体的fmt::Debug和fmt::Displays实现

  1. Color结构

    #[derive(Debug)]
    struct Color{
        red: u8,
        green: u8,
        blue: u8,
    }
    
  2. 实现Color结构的的fmt::Debug和fmt::Display

    impl fmt::Display for Color{
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
            write!(f, "RGB({},{},{}) 0x{:02X}{:02X}{:02X}",
                self.red, self.green, self.blue,
                self.red, self.green, self.blue)
        }
    }
    
  3. 测试输出

    fn main(){
        let color = Color{ red: 223, green: 52, blue: 77 };
        println!("Debug Color print:{:?}", color);
        println!("Display Color print:{}", color);    
        println!("----------loop_color----------");
        for color in [
            Color { red: 128, green: 255, blue: 90 },
            Color { red: 0, green: 3, blue: 254 },
            Color { red: 0, green: 0, blue: 0 },
        ].iter() {
            println!("Debug Color print:{:?}", *color);
            println!("Display Color print:{}", *color);
        }
    }
    //输出结果----------loop_color----------
    //Debug Color print:Color { red: 128, green: 255, blue: 90 }
    //Display Color print:RGB(128,255,90) 0x80FF5A
    //Debug Color print:Color { red: 0, green: 3, blue: 254 }
    //Display Color print:RGB(0,3,254) 0x0003FE
    //Debug Color print:Color { red: 0, green: 0, blue: 0 }
    //Display Color print:RGB(0,0,0) 0x000000
    
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值