rust——Struct、Trait练习记录

Rust homework2

题目要求

请用rust完成下面题目:
题目:几何形状管理程序(考察Struct、Trait、Generic的用法)
要求:

  1. 创建一个名为Shape的Trait,其中包括以下方法:

    1. area(&self) -> f64:计算几何形状的面积。
    2. perimeter(&self) -> f64:计算几何形状的周长。
  2. 创建三个Struct,分别代表以下几何形状,每个Struct都必须实现Shape Trait:

    1. 矩形(Rectangle):包含长度和宽度。
    2. 圆形(Circle):包含半径。
    3. 三角形(Triangle):包含三条边的长度。
  3. 创建一个泛型函数print_shape_info<T: Shape>(shape: T),它接受任何实现了Shape Trait的几何形状,然后打印该几何形状的类型、面积和周长。

  4. 在main函数中,创建至少一个矩形、一个圆形和一个三角形的实例,并使用print_shape_info函数分别输出它们的信息。

提示:
•在Shape Trait中,你可以使用关联类型来定义几何形状的属性,例如面积和周长的类型。这样,每个实现Trait的类型可以定义自己的关联类型。
•在main函数中,可以使用泛型函数print_shape_info来减少代码重复。
这个作业将考察学生对Trait、Struct、以及泛型的理解和运用。学生需要创建自定义的Struct,并确保它们实现了Trait中定义的方法。同时,他们需要编写一个泛型函数来处理不同类型的几何形状,并灵活地使用Trait来计算面积和周长。这有助于加强对Rust中泛型和Trait系统的理解。

尝试

代码已经写完了,分为两个文件shape.rs和main.rs:

// shape.rs
use std::fmt;

trait Shape {
    
    
    type Area; // 面积
    type Perimeter; // 周长
    fn area(&self) -> Self::Area;
    fn perimeter(&self) -> Self::Perimeter;
}

struct Rectangle(f64, f64);
impl Shape for Rectangle {
    
    
    type Area = f64;
    type Perimeter = f64;
    fn area(&self) -> Self::Area {
    
    
        self.0 * self.1 // 长×宽
    }
    fn perimeter(&self) -> Self::Perimeter {
    
    
        2.0 * (self.0 + self.1) // 2×(长+宽)
    }
}

struct Circle(f64);
impl Shape for Circle {
    
    
    type Area = f64;
    type Perimeter = f64;
    fn area(&self) -> Self::Area {
    
    
        // πr*r
        std::f64::consts::PI * self.0 * self.0
    }
    fn perimeter(&self) -> Self::Perimeter {
    
    
        // 2πr
        2.0 * std::f64::consts::PI * self.0
    }
}

struct Triangle(f64, f64, f64);
// 判断构造时输入三边能否构成一个三角形
impl Triangle {
    
    
    fn new(a: f64, b: f64, c: f64) -> Result<Self, &'static str> {
    
    
        if a + b > c && a + c > b && b + c > a {
    
    
            Ok(Triangle(a, b, c))
        } else {
    
    
            Err("三角形三边长必须满足任意两边和大于第三边")
        }
    }
}

impl Shape for Triangle {
    
    
    type Area = f64;
    type Perimeter = f64;
    fn area(&self) -> Self::Area {
    
    
        // 方法一:海伦公式
        // 设三角形的三边长分别为a、b、c,且s为半周长,即s = (a + b + c) / 2
        // 那么三角形面积=√(s*(s-a)*(s-b)*(s-c))
        let s = (self.0 + self.1 + self.2) / 2.0;
        (s * (s - self.0) * (s - self.1) * (s - self.2)).sqrt()
    }
    fn perimeter(&self) -> Self::Perimeter {
    
    
        self.0 + self.1 + self.2
    }
}

// 泛型函数print_shape_info,
// 接受任何实现了Shape trait的几何形状,打印其信息。
fn print_shape_info<T: Shape>(shape: &T) {
    
    
    println!("形状:{}", shape);
    println!("面积:{}", shape.area());
    println!("周长:{}", shape.perimeter());
}

// main.rs
mod shape;
fn main() {
    
    
    let rectangle = shape::Rectangle(10.0,20.0);
    let circle = shape::Circle(10.0);
    let triangle = shape::Triangle(15.0,20.0,30.0);
    shape::print_shape_info(&rectangle);
    shape::print_shape_info(&circle);
    shape::print_shape_info(&triangle);
}

但是编译的时候出现了很多报错:

error[E0573]: expected type, found module `self`
  --> shape.rs:39:46
   |
39 |     fn new(a: f64, b: f64, c: f64) -> Result<self, &'static str> {
   |                                              ^^^^ help: a self type with a similar name exists (notice the capitalization): `Self`

error[E0603]: tuple struct constructor `Rectangle` is private
  --> main.rs:3:28
   |
3  |     let rectangle = shape::Rectangle(10.0,20.0);
   |                            ^^^^^^^^^ private tuple struct constructor
   |
  ::: shape.rs:10:18
   |
10 | struct Rectangle(f64, f64);
   |                  -------- a constructor is private if any of the fields is private
   |
note: the tuple struct constructor `Rectangle` is defined here
  --> shape.rs:10:1
   |
10 | struct Rectangle(f64, f64);
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider making the fields publicly accessible
  --> shape.rs:10:18
   |
10 | struct Rectangle(pub f64, pub f64);
   |                  +++      +++

error[E0603]: tuple struct constructor `Circle` is private
  --> main.rs:4:25
   |
4  |     let circle = shape::Circle(10.0);
   |                         ^^^^^^ private tuple struct constructor
   |
  ::: shape.rs:22:15
   |
22 | struct Circle(f64);
   |               --- a constructor is private if any of the fields is private
   |
note: the tuple struct constructor `Circle` is defined here
  --> shape.rs:22:1
   |
22 | struct Circle(f64);
   | ^^^^^^^^^^^^^^^^^^^
help: consider making the field publicly accessible
  --> shape.rs:22:15
   |
22 | struct Circle(pub f64);
   |               +++

error[E0603]: tuple struct constructor `Triangle` is private
  --> main.rs:5:27
   |
5  |     let triangle = shape::Triangle(15.0,20.0,30.0);
   |                           ^^^^^^^^ private tuple struct constructor
   |
  ::: shape.rs:36:17
   |
36 | struct Triangle(f64, f64, f64);
   |                 ------------- a constructor is private if any of the fields is private
   |
note: the tuple struct constructor `Triangle` is defined here
  --> shape.rs:36:1
   |
36 | struct Triangle(f64, f64, f64);
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider making the fields publicly accessible
  --> shape.rs:36:17
   |
36 | struct Triangle(pub f64, pub f64, pub f64);
   |                 +++      +++      +++

error[E0603]: function `print_shape_info` is private
  --> main.rs:6:12
   |
6  |     shape::print_shape_info(&rectangle);
   |            ^^^^^^^^^^^^^^^^ private function
   |
note: the function `print_shape_info` is defined here
  --> shape.rs:65:1
   |
65 | fn print_shape_info<T: Shape>(shape: &T) {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0603]: function `print_shape_info` is private
  --> main.rs:7:12
   |
7  |     shape::print_shape_info(&circle);
   |            ^^^^^^^^^^^^^^^^ private function
   |
note: the function `print_shape_info` is defined here
  --> shape.rs:65:1
   |
65 | fn print_shape_info<T: Shape>(shape: &T) {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0603]: function `print_shape_info` is private
  --> main.rs:8:12
   |
8  |     shape::print_shape_info(&triangle);
   |            ^^^^^^^^^^^^^^^^ private function
   |
note: the function `print_shape_info` is defined here
  --> shape.rs:65:1
   |
65 | fn print_shape_info<T: Shape>(shape: &T) {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: unused import: `std::fmt`
 --> shape.rs:1:5
  |
1 | use std::fmt;
  |     ^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error[E0277]: `T` doesn't implement `std::fmt::Display`
  --> shape.rs:66:23
   |
66 |     println!("形状:{}", shape);
   |                          ^^^^^ `T` cannot be formatted with the default formatter
   |
   = note: in format strings you may be able to use `{
     
     :?}` (or {
    
    :#?} for pretty-print) instead
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting this bound
   |
65 | fn print_shape_info<T: Shape + std::fmt::Display>(shape: &T) {
    
    
   |                              +++++++++++++++++++

error[E0277]: `<T as Shape>::Area` doesn't implement `std::fmt::Display`
  --> shape.rs:67:23
   |
67 |     println!("面积:{}", shape.area());
   |                          ^^^^^^^^^^^^ `<T as Shape>::Area` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `<T as Shape>::Area`
   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting the associated type
   |
65 | fn print_shape_info<T: Shape>(shape: &T) where <T as Shape>::Area: std::fmt::Display {
   |                                          +++++++++++++++++++++++++++++++++++++++++++

error[E0277]: `<T as Shape>::Perimeter` doesn't implement `std::fmt::Display`
  --> shape.rs:68:23
   |
68 |     println!("周长:{}", shape.perimeter());
   |                          ^^^^^^^^^^^^^^^^^ `<T as Shape>::Perimeter` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `<T as Shape>::Perimeter`
   = note: in format strings you may be able to use `{
     
     :?}` (or {
    
    :#?} for pretty-print) instead
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting the associated type
   |
65 | fn print_shape_info<T: Shape>(shape: &T) where <T as Shape>::Perimeter: std::fmt::Display {
    
    
   |                                          ++++++++++++++++++++++++++++++++++++++++++++++++

error: aborting due to 10 previous errors; 1 warning emitted

Some errors have detailed explanations: E0277, E0573, E0603.
For more information about an error, try `rustc --explain E0277`.

[Done] exited with code=1 in 0.387 seconds

调试与修改

虽然报错很长,但是仔细分析一下其实只有三类错误:E0277,E0573和E0603.
而且一些报错也给出了修正的建议,这些建议刚好也点中了问题,所以修改起来还是比较容易的。

比如对于错误E0573

error[E0573]: expected type, found module self
–> shape.rs:39:46
|
39 | fn new(a: f64, b: f64, c: f64) -> Result<self, &'static str> {
| ^^^^ help: a self type with a similar name exists (notice the capitalization): Self

就说这个想要一个type类型,但是我填的是一个module名,然后编译器猜测我应该填的是Self这个类型。

再比如对于错误E0603

error[E0603]: tuple struct constructor Rectangle is private
–> main.rs:3:28
|
3 | let rectangle = shape::Rectangle(10.0,20.0);
| ^^^^^^^^^ private tuple struct constructor
|
::: shape.rs:10:18
|
10 | struct Rectangle(f64, f64);
| -------- a constructor is private if any of the fields is private
|
note: the tuple struct constructor Rectangle is defined here
–> shape.rs:10:1
|
10 | struct Rectangle(f64, f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider making the fields publicly accessible
–> shape.rs:10:18
|
10 | struct Rectangle(pub f64, pub f64);
| +++ +++

这里说我调用了一个私有构造函数,但这是不被允许的,然后编译器建议我修改为公有的,并给出了具体的修改代码建议:
struct Rectangle(f64, f64);

struct Rectangle(pub f64, pub f64);

不过经过测试发现这样并不能解决问题

error[E0603]: tuple struct constructor `Rectangle` is private
  --> main.rs:4:28
   |
4  |     let rectangle = shape::Rectangle(10.0,20.0);
   |                            ^^^^^^^^^ private tuple struct constructor
   |
  ::: shape.rs:11:18
   |
11 | struct Rectangle(pub f64, pub f64);
   |                  ---------------- a constructor is private if any of the fields is private
   |
note: the tuple struct constructor `Rectangle` is defined here
  --> shape.rs:11:1
   |
11 | struct Rectangle(pub f64, pub f64);
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider making the fields publicly accessible
  --> shape.rs:11:18
   |
11 | struct Rectangle(pub f64, pub f64);
   |                  ~~~      ~~~

网上查了一下资料,正确是改为pub struct Rectangle(pub f64, pub f64);

还有一个E0277的错误,大概是因为
fn area(&self) -> Self::Area
fn perimeter(&self) -> Self::Perimeter
这样的写法会识别不出返回的类型,
不过改为了fn area(&self) -> f64
fn perimeter(&self) -> f64
发现还是不行。

经过一番试错(期间问过ChatGPT,但是它并不能给我写出正确答案,感觉是rust更新太快了,ChatGPT跟不上),最终得到了解决方案:

trait Shape {
    
    
    type Area; // 面积
    type Perimeter; // 周长
    fn area(&self) -> Self::Area;
    fn perimeter(&self) -> Self::Perimeter;
}

这里的Shape trait修改一下,给type Area和type Perimeter加上fmt::Display的trait

pub trait Shape {
    
    
    type Area: fmt::Display; // 面积,这里要加上fmt::Display的特性,否则在Println!的时候会被认为不能匹配该特性
    type Perimeter: fmt::Display; // 周长
    fn area(&self) -> Self::Area;
    fn perimeter(&self) -> Self::Perimeter;
}

此外,为了让print_shape_info泛型函数可以打印出形状,或者说是传入的T的类型,修改为:

pub fn print_shape_info<T: Shape>(shape: &T) {
    
    
    println!("形状:{}", std::any::type_name::<T>());
    println!("面积:{}", shape.area());
    println!("周长:{}", shape.perimeter());
}

其实就是使用std::any::type_name::()来打印出传入的T的类型名,因为类型名对应着这个shape的具体形状类别。

最后也修改了一下main函数,让打印的信息更完善:

// main.rs
mod shape;
fn main() {
    
    
    let rectangle = shape::Rectangle(10.0,20.0);
    let circle = shape::Circle(10.0);
    let triangle = shape::Triangle(15.0,20.0,30.0);
    println!("矩形信息:");
    shape::print_shape_info(&rectangle);
    println!("圆形信息:");
    shape::print_shape_info(&circle);
    println!("三角形信息:");
    shape::print_shape_info(&triangle);
}

成功运行

运行后的输出结果:

[Running] cd "d:\wuyh文档\大三课程\大三上\rust与内存安全\project\homework2\homework2\src\" && rustc main.rs && "d:\wuyh文档\大三课程\大三上\rust与内存安全\project\homework2\homework2\src\"main
warning: associated function `new` is never used
  --> shape.rs:41:8
   |
40 | impl Triangle {
    
    
   | ------------- associated function in this implementation
41 |     fn new(a: f64, b: f64, c: f64) -> Result<Self, &'static str> {
    
    
   |        ^^^
   |
   = note: `#[warn(dead_code)]` on by default

warning: 1 warning emitted

矩形信息:
形状:main::shape::Rectangle
面积:200
周长:60
圆形信息:
形状:main::shape::Circle
面积:314.1592653589793
周长:62.83185307179586
三角形信息:
形状:main::shape::Triangle
面积:133.31705629813464
周长:65

[Done] exited with code=0 in 0.864 seconds

发现新的错误

尝试给三角形实例构建输入一个不正确的三边,发现并没有报错,分析这是因为没有用triangle的new函数来创建对象。

修改main.rs:

// main.rs
mod shape;
fn main() {
    
    
    let rectangle = shape::Rectangle(10.0,20.0);
    let circle = shape::Circle(10.0);
    let triangle = shape::Triangle::new(10.0,20.0,30.0);
    println!("矩形信息:");
    shape::print_shape_info(&rectangle);
    println!("圆形信息:");
    shape::print_shape_info(&circle);
    println!("三角形信息:");
    shape::print_shape_info(&triangle);
}

编译的时候出现报错:

error[E0624]: associated function `new` is private
  --> main.rs:6:37
   |
6  |     let triangle = shape::Triangle::new(10.0,20.0,30.0);
   |                                     ^^^ private associated function
   |
  ::: shape.rs:41:5
   |
41 |     fn new(a: f64, b: f64, c: f64) -> Result<Self, &'static str> {
    
    
   |     ------------------------------------------------------------ private associated function defined here

error[E0277]: the trait bound `Result<Triangle, &str>: Shape` is not satisfied
  --> main.rs:12:29
   |
12 |     shape::print_shape_info(&triangle);
   |     ----------------------- ^^^^^^^^^ the trait `Shape` is not implemented for `Result<Triangle, &str>`
   |     |
   |     required by a bound introduced by this call
   |
   = help: the following other types implement trait `Shape`:
             Rectangle
             Circle
             Triangle
note: required by a bound in `print_shape_info`
  --> shape.rs:72:28
   |
72 | pub fn print_shape_info<T: Shape>(shape: &T) {
    
    
   |                            ^^^^^ required by this bound in `print_shape_info`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0277, E0624.
For more information about an error, try `rustc --explain E0277`.

上面有两个报错:E0277, E0624

第一个报错E0277是说函数new是私有的。我们可以尝试改为共有。也就是在fn new(a: f64, b: f64, c: f64) -> Result<Self, &'static str> 的前面加上pub修饰即可。

第二个报错E0624大概是shape::print_shape_info(&triangle);的triangle如果使用new函数创建的话不能直接使用print_shape_info。而是要根据返回的结果进行判断。

所以main.rs要修改为:

// main.rs
mod shape;
fn main() {
    
    
    let rectangle = shape::Rectangle(10.0, 20.0);
    let circle = shape::Circle(10.0);
    let triangle_result = shape::Triangle::new(10.0, 20.0, 30.0);
    println!("矩形信息:");
    shape::print_shape_info(&rectangle);
    println!("圆形信息:");
    shape::print_shape_info(&circle);
    println!("三角形信息:");
    match triangle_result {
    
    
        Ok(triangle) => {
    
    //把ok的结果保存在triangle中,方便打印输出
            shape::print_shape_info(&triangle);
        }
        Err(e) => println!("Error: {}", e),// 打印错误信息
    }
}

这样就能处理shape::Triangle::new(10.0, 20.0, 30.0)返回的结果,根据结果进一步决定打印三角形信息还是报错。

猜你喜欢

转载自blog.csdn.net/weixin_43469174/article/details/134280264