Rust homework2
题目要求
请用rust完成下面题目:
题目:几何形状管理程序(考察Struct、Trait、Generic的用法)
要求:
-
创建一个名为Shape的Trait,其中包括以下方法:
- area(&self) -> f64:计算几何形状的面积。
- perimeter(&self) -> f64:计算几何形状的周长。
-
创建三个Struct,分别代表以下几何形状,每个Struct都必须实现Shape Trait:
- 矩形(Rectangle):包含长度和宽度。
- 圆形(Circle):包含半径。
- 三角形(Triangle):包含三条边的长度。
-
创建一个泛型函数print_shape_info<T: Shape>(shape: T),它接受任何实现了Shape Trait的几何形状,然后打印该几何形状的类型、面积和周长。
-
在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)返回的结果,根据结果进一步决定打印三角形信息还是报错。