🦀

2023/06/10に公開

# これは何

しかしただ読むだけだと片っ端から以前の内容を忘れていき身になった感じがしないため、ChatGPTに練習問題を作成してもらうことにしました。
ここではChatGPTがどんな問題を作ってくれて、それに対してどう回答したかみたいな内容を記録していこうと思っています。

## Enum編

GW挟んだせいでEnumについて全て忘れました。もう一回読み直します

## 読み直した上で練習問題

ChatGPTに練習問題作ってくれないか相談

I'm trying to get myself familiar with enum in Rust.
Can you give me some exercises??

Exercise 1:
Create an enum called Direction that represents the four cardinal directions: North, South, East, and West. Implement a function called opposite_direction that takes a Direction as input and returns the opposite direction. For example, if the input is Direction::North, the function should return Direction::South.

Exercise 2:
Create an enum called Shape that represents different shapes: Circle, Square, and Triangle. Implement a function called calculate_area that takes a Shape as input and returns the area of the shape. Assume that the shapes are centered at the origin and have a unit radius/side length. You can use simple formulas to calculate the areas (e.g., πr^2 for a circle).

Exercise 3:
Create an enum called Result<T, E> that represents the result of an operation. It should have two variants: Ok(T) to indicate a successful result with a value of type T, and Err(E) to indicate an error with a value of type E. Implement a function called divide that takes two integers as input and returns a Result indicating the result of the division. If the second integer is zero, return an Err variant with an appropriate error message; otherwise, return an Ok variant with the result of the division.

## Exercise 1の回答

`Direction`というenumを作成し、方角をvariantとするという問題。

とりあえず以下のように実装してみました。

``````fn main() {
#[derive(Debug)]
enum Direction {
North,
East,
South,
West
}

impl Direction {
fn opposite(&self) -> Direction {
match self {
Direction::North => Direction::South,
Direction::East => Direction::West,
Direction::South => Direction::North,
Direction::West => Direction::East,
}
}
}

let north = Direction::North;
println!("{:?}", north.opposite());
}
``````

ChatGPTの反応。褒めてくれる、うれしい。

Your solution for Exercise 1 looks great! You've defined the Direction enum with its four variants: North, East, South, and West. The opposite method is implemented for the Direction enum, allowing you to find the opposite direction based on the current direction.

しかしここで、「あれこれassociated functionとして定義したけど、別にその必要なくてむしろ普通のfunctionとして作成した方がよかったのでは、、？」という疑問が。
ChatGPTに聞いてみると、

I apologize for any confusion caused by the way the question was presented. You are correct that the wording of the exercise could have been clearer. In the exercise, you were asked to implement a function called opposite_direction that takes a Direction as input and returns the opposite direction.

While implementing opposite_direction as an associated function of the Direction enum is one way to approach the exercise, it is not the only valid approach. Defining opposite_direction as a standalone function would be perfectly acceptable as well.

Here's an example of implementing opposite_direction as a standalone function:

``````#[derive(Debug)]
enum Direction {
North,
South,
East,
West,
}

fn opposite_direction(dir: Direction) -> Direction {
match dir {
Direction::North => Direction::South,
Direction::South => Direction::North,
Direction::East => Direction::West,
Direction::West => Direction::East,
}
}

fn main() {
let current_direction = Direction::North;
let opposite = opposite_direction(current_direction);
println!("Opposite direction: {:?}", opposite);
println!("Current direction: {:?}", current_direction);
}
``````

ChatGPT的にはどっちでもよかったらしく、methodではない実装例をあげてくれた。

## Exercise 2の回答

`Shape`というenumを作成し、variantには`Circle``Square``Triangle`を。

``````fn main() {
#[derive(Debug)]
enum Shape {
Circle {
},
Square {
width: f64,
height: f64
},
Triangle {
base: f64,
height: f64
}
}

impl Shape {
fn calculate_area(&self) -> f64 {
match self {
Shape::Square {width, height} => width * height,
Shape::Triangle {base, height} => (base * height) / 2.0,
}
}
}

let circle = Shape::Circle {radius: 3.0};
println!("Circle: {:?}", circle.calculate_area());

let square = Shape::Square {width: 2.0, height: 2.0};
println!("Square: {:?}", square.calculate_area());

let triangle = Shape::Triangle {base: 5.0, height: 20.0};
println!("Triangle: {:?}", triangle.calculate_area());
}
``````

またしても褒めてくれる。

Your solution for Exercise 2 is well done! You've defined the Shape enum with three variants: Circle, Square, and Triangle. Each variant contains the necessary fields to represent the corresponding shape.

しかしながらより良い解があるのではと、実装例を見せて欲しいとお願いすると

``````enum Shape {
Circle(f64),
Square(f64),
Triangle(f64, f64),
}

impl Shape {
fn calculate_area(&self) -> f64 {
match self {
Shape::Square(side_length) => side_length.powi(2),
Shape::Triangle(base, height) => 0.5 * base * height,
}
}
}

fn main() {
let circle = Shape::Circle(3.0);
println!("Circle: {:?}", circle.calculate_area());

let square = Shape::Square(2.0);
println!("Square: {:?}", square.calculate_area());

let triangle = Shape::Triangle(5.0, 20.0);
println!("Triangle: {:?}", triangle.calculate_area());
}
``````

## Exercise 3の回答

`Result<T, E>`というenumを作成し、variantには`Okay(T)``Err(E)`を。

• 2つ目のパラメータが`0`の場合`Err`でラップしたエラーメッセージを返す。
• それ以外の場合は割り算の結果を`Okay`でラップして返す

``````fn main() {
#[derive(Debug)]
enum Result<T, E> {
Okay(T),
Err(E)
}

fn divide(num1: i32, num2: i32) -> Result<i32, String> {
match num2 {
0 => Result::Err(String::from("Can't take 0, sorry m8")),
_ => Result::Okay(num1 / num2)
}
}

let calc1 = divide(9, 3);
println!("Calc 1: {:?}", calc1);

let calc2 = divide(5, 0);
println!("Calc 2: {:?}", calc2);
}
``````

やっぱり褒めてくれる。

Your solution for Exercise 3 is well done! You've defined the Result<T, E> enum, which is similar to the built-in Result enum in Rust. It has two variants: Okay(T) to represent a successful result with a value of type T, and Err(E) to represent an error with a value of type E.

ChatGPTにも実装をするようお願いすると、

``````enum Result<T, E> {
Ok(T),
Err(E),
}

fn divide(num1: i32, num2: i32) -> Result<i32, String> {
if num2 == 0 {
Result::Err(String::from("Can't divide by zero"))
} else {
Result::Ok(num1 / num2)
}
}

fn main() {
let calc1 = divide(9, 3);
match calc1 {
Result::Ok(result) => println!("Calc 1: {:?}", result),
Result::Err(err) => println!("Calc 1 error: {:?}", err),
}

let calc2 = divide(5, 0);
match calc2 {
Result::Ok(result) => println!("Calc 2: {:?}", result),
Result::Err(err) => println!("Calc 2 error: {:?}", err),
}
}
``````

`devide`の中では`if`を使ってその後`Result`から帰ってきた値を抜き出すところで`match`を使ってる。

`match`の使い所もこっちの方が正しい気がする