Skip to content
Snippets Groups Projects
Commit e451a751 authored by Fabio Vandewaeter's avatar Fabio Vandewaeter
Browse files

TP3 exo1 et exo2

parent 6e25bbfe
Branches
No related tags found
No related merge requests found
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "tp3"
version = "0.1.0"
[package]
name = "tp3"
version = "0.1.0"
edition = "2024"
[dependencies]
# COA-VANDEWAETER
Dépôt : https://gitlab.univ-lille.fr/fabio.vandewaeter.etu/coa-vandewaeter
# TP3
src :
Lancer les programmes :
```bash
rustc src/problem1.rs -o prog && ./prog
```
# 1. Traits
## 1.1. Creating traits
### 1.1.1. A simple encoding/decoding trait
```rs
trait Encoder {
fn encode(&self, message: &str) -> String;
}
trait Decoder {
fn decode(&self, message: &str) -> String;
}
```
### 1.1.2. A first encoder
```rs
impl Encoder for ToUpper {
fn encode(&self, message: &str) -> String {
message.to_uppercase()
}
}
impl Encoder for ToLower {
fn encode(&self, message: &str) -> String {
message.to_lowercase()
}
}
```
### 1.1.3. Two traits for one type
```rs
struct Caesar {
shift: u8,
}
impl Encoder for Caesar {
fn encode(&self, message: &str) -> String {
message
.chars()
.map(|c| {
if c.is_ascii() {
let shifted = (c as u8 + self.shift) % 128;
shifted as char
} else {
c
}
})
.collect()
}
}
impl Decoder for Caesar {
fn decode(&self, message: &str) -> String {
message
.chars()
.map(|c| {
if c.is_ascii() {
let shifted = (c as u8 + 128 - self.shift) % 128;
shifted as char
} else {
c
}
})
.collect()
}
}
```
### 1.1.4. Implement a trait for a foreign type
```rs
trait CaesarExt {
/// Encode self using a Caesar's cypher with the given shift
fn encode(&self, shift: u8) -> String;
/// Decode self using a Caesar's cypher with the given shift
fn decode(&self, shift: u8) -> String;
}
impl CaesarExt for str {
fn encode(&self, shift: u8) -> String {
let caesar = Caesar { shift };
caesar.encode(self)
}
fn decode(&self, shift: u8) -> String {
let caesar = Caesar { shift };
caesar.decode(self)
}
}
```
## 1.2. Implementing common traits
### 1.2.1. à 1.2.4. Clone/Copy/Debug/PartialEq
On peut gérer ces 4 cas simplement en utilisant `derive` :
```rs
#[derive(Clone, Copy, Debug, PartialEq)]
enum Instruction {
/// Integer addition
Add(i32, i32),
/// Integer multiplication
Mul(i32, i32),
/// Floating point addition
FAdd(f32, f32),
/// Floating point multiplication
FMul(f32, f32),
}
```
### 1.2.5. Display
```rs
impl std::fmt::Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Instruction::Add(a, b) => write!(f, "{} + {}", a, b),
Instruction::Mul(a, b) => write!(f, "{} * {}", a, b),
Instruction::FAdd(a, b) => write!(f, "{} + {}", a, b),
Instruction::FMul(a, b) => write!(f, "{} * {}", a, b),
}
}
}
```
### 1.2.6. FromStr
```rs
#[derive(Debug, PartialEq)]
enum InstructionError {
MalformedExpression,
UnknownOperator,
ParseOperandError(String),
}
impl std::str::FromStr for Instruction {
type Err = InstructionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split_whitespace().collect();
if parts.len() != 3 {
return Err(InstructionError::MalformedExpression);
}
let op = parts[1];
let (a_str, b_str) = (parts[0], parts[2]);
let is_float = a_str.contains('.') || b_str.contains('.');
if is_float {
let a = a_str.parse().map_err(|e: std::num::ParseFloatError| {
InstructionError::ParseOperandError(e.to_string())
})?;
let b = b_str.parse().map_err(|e: std::num::ParseFloatError| {
InstructionError::ParseOperandError(e.to_string())
})?;
match op {
"+" => Ok(Instruction::FAdd(a, b)),
"*" => Ok(Instruction::FMul(a, b)),
_ => Err(InstructionError::UnknownOperator),
}
} else {
let a = a_str.parse().map_err(|e: std::num::ParseIntError| {
InstructionError::ParseOperandError(e.to_string())
})?;
let b = b_str.parse().map_err(|e: std::num::ParseIntError| {
InstructionError::ParseOperandError(e.to_string())
})?;
match op {
"+" => Ok(Instruction::Add(a, b)),
"*" => Ok(Instruction::Mul(a, b)),
_ => Err(InstructionError::UnknownOperator),
}
}
}
}
```
### 1.2.7. TryFrom<&[u8]> for Instruction
```rs
#[derive(Debug)]
enum TryFromInstructionError {
InvalidLength,
InvalidOpcode(u8),
}
impl TryFrom<&[u8]> for Instruction {
type Error = TryFromInstructionError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
if value.len() < 9 {
return Err(TryFromInstructionError::InvalidLength);
}
let opcode = value[0];
let (a_bytes, b_bytes) = (&value[1..5], &value[5..9]);
match opcode {
0 => Ok(Instruction::Add(
i32::from_le_bytes(a_bytes.try_into().unwrap()),
i32::from_le_bytes(b_bytes.try_into().unwrap()),
)),
1 => Ok(Instruction::Mul(
i32::from_le_bytes(a_bytes.try_into().unwrap()),
i32::from_le_bytes(b_bytes.try_into().unwrap()),
)),
2 => Ok(Instruction::FAdd(
f32::from_le_bytes(a_bytes.try_into().unwrap()),
f32::from_le_bytes(b_bytes.try_into().unwrap()),
)),
3 => Ok(Instruction::FMul(
f32::from_le_bytes(a_bytes.try_into().unwrap()),
f32::from_le_bytes(b_bytes.try_into().unwrap()),
)),
_ => Err(TryFromInstructionError::InvalidOpcode(opcode)),
}
}
}
```
### 1.2.8. From<Instruction> for [u8; 9]
```rs
impl From<Instruction> for [u8; 9] {
fn from(inst: Instruction) -> Self {
let mut res = [0u8; 9];
match inst {
Instruction::Add(a, b) => {
res[0] = 0;
res[1..5].copy_from_slice(&a.to_le_bytes());
res[5..9].copy_from_slice(&b.to_le_bytes());
}
Instruction::Mul(a, b) => {
res[0] = 1;
res[1..5].copy_from_slice(&a.to_le_bytes());
res[5..9].copy_from_slice(&b.to_le_bytes());
}
Instruction::FAdd(a, b) => {
res[0] = 2;
res[1..5].copy_from_slice(&a.to_le_bytes());
res[5..9].copy_from_slice(&b.to_le_bytes());
}
Instruction::FMul(a, b) => {
res[0] = 3;
res[1..5].copy_from_slice(&a.to_le_bytes());
res[5..9].copy_from_slice(&b.to_le_bytes());
}
}
res
}
}
```
### 1.2.9. Tests
```shell
cargo test
```
# 2. Generics
## 2.1. Without trait bounds
```rs
enum Either<L, R> {
Left(L),
Right(R),
}
impl<L, R> Either<L, R> {
/// Consume self and return option that contains the left variant if any
fn left(self) -> Option<L> {
match self {
Either::Left(l) => Some(l),
Either::Right(_) => None,
}
}
/// Consume self and return option that contains the right variant if any
fn right(self) -> Option<R> {
match self {
Either::Right(r) => Some(r),
Either::Left(_) => None,
}
}
/// Returns true if self is the left variant
fn is_left(&self) -> bool {
match self {
Either::Left(_) => true,
Either::Right(_) => false,
}
}
/// Returns true if self is the right variant
fn is_right(&self) -> bool {
match self {
Either::Right(_) => true,
Either::Left(_) => false,
}
}
/// Returns a new Either that that holds reference to this either inner value
fn as_ref(&self) -> Either<&L, &R> {
match self {
Either::Left(l) => Either::Left(l),
Either::Right(r) => Either::Right(r),
}
}
}
```
## 2.2. With trait bounds
```rs
```
# 3. Long exercise proposal
\ No newline at end of file
// ====================== 1. Traits ======================
// 1.1. Creating traits
// 1.1.1. A simple encoding/decoding trait
trait Encoder {
fn encode(&self, message: &str) -> String;
}
trait Decoder {
fn decode(&self, message: &str) -> String;
}
// 1.1.2. A first encoder
struct ToUpper {}
struct ToLower {}
impl Encoder for ToUpper {
fn encode(&self, message: &str) -> String {
message.to_uppercase()
}
}
impl Encoder for ToLower {
fn encode(&self, message: &str) -> String {
message.to_lowercase()
}
}
// 1.1.3. Two traits for one type
struct Caesar {
shift: u8,
}
impl Encoder for Caesar {
fn encode(&self, message: &str) -> String {
message
.chars()
.map(|c| {
if c.is_ascii() {
let shifted = (c as u8 + self.shift) % 128;
shifted as char
} else {
c
}
})
.collect()
}
}
impl Decoder for Caesar {
fn decode(&self, message: &str) -> String {
message
.chars()
.map(|c| {
if c.is_ascii() {
let shifted = (c as u8 + 128 - self.shift) % 128;
shifted as char
} else {
c
}
})
.collect()
}
}
// 1.1.4. Implement a trait for a foreign type
trait CaesarExt {
/// Encode self using a Caesar's cypher with the given shift
fn encode(&self, shift: u8) -> String;
/// Decode self using a Caesar's cypher with the given shift
fn decode(&self, shift: u8) -> String;
}
impl CaesarExt for str {
fn encode(&self, shift: u8) -> String {
let caesar = Caesar { shift };
caesar.encode(self)
}
fn decode(&self, shift: u8) -> String {
let caesar = Caesar { shift };
caesar.decode(self)
}
}
// 1.2. Implementing common traits
// 1.2.1. à 1.2.4. Clone/Copy/Debug/PartialEq
/// An enumeration that represents an instruction to execute
#[derive(Clone, Copy, Debug, PartialEq)]
enum Instruction {
/// Integer addition
Add(i32, i32),
/// Integer multiplication
Mul(i32, i32),
/// Floating point addition
FAdd(f32, f32),
/// Floating point multiplication
FMul(f32, f32),
}
// 1.2.5. Display
impl std::fmt::Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Instruction::Add(a, b) => write!(f, "{} + {}", a, b),
Instruction::Mul(a, b) => write!(f, "{} * {}", a, b),
Instruction::FAdd(a, b) => write!(f, "{} + {}", a, b),
Instruction::FMul(a, b) => write!(f, "{} * {}", a, b),
}
}
}
// 1.2.6. FromStr
#[derive(Debug, PartialEq)]
enum InstructionError {
MalformedExpression,
UnknownOperator,
ParseOperandError(String),
}
impl std::str::FromStr for Instruction {
type Err = InstructionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split_whitespace().collect();
if parts.len() != 3 {
return Err(InstructionError::MalformedExpression);
}
let op = parts[1];
let (a_str, b_str) = (parts[0], parts[2]);
let is_float = a_str.contains('.') || b_str.contains('.');
if is_float {
let a = a_str.parse().map_err(|e: std::num::ParseFloatError| {
InstructionError::ParseOperandError(e.to_string())
})?;
let b = b_str.parse().map_err(|e: std::num::ParseFloatError| {
InstructionError::ParseOperandError(e.to_string())
})?;
match op {
"+" => Ok(Instruction::FAdd(a, b)),
"*" => Ok(Instruction::FMul(a, b)),
_ => Err(InstructionError::UnknownOperator),
}
} else {
let a = a_str.parse().map_err(|e: std::num::ParseIntError| {
InstructionError::ParseOperandError(e.to_string())
})?;
let b = b_str.parse().map_err(|e: std::num::ParseIntError| {
InstructionError::ParseOperandError(e.to_string())
})?;
match op {
"+" => Ok(Instruction::Add(a, b)),
"*" => Ok(Instruction::Mul(a, b)),
_ => Err(InstructionError::UnknownOperator),
}
}
}
}
// 1.2.7. TryFrom<&[u8]> for Instruction
#[derive(Debug)]
enum TryFromInstructionError {
InvalidLength,
InvalidOpcode(u8),
}
impl TryFrom<&[u8]> for Instruction {
type Error = TryFromInstructionError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
if value.len() < 9 {
return Err(TryFromInstructionError::InvalidLength);
}
let opcode = value[0];
let (a_bytes, b_bytes) = (&value[1..5], &value[5..9]);
match opcode {
0 => Ok(Instruction::Add(
i32::from_le_bytes(a_bytes.try_into().unwrap()),
i32::from_le_bytes(b_bytes.try_into().unwrap()),
)),
1 => Ok(Instruction::Mul(
i32::from_le_bytes(a_bytes.try_into().unwrap()),
i32::from_le_bytes(b_bytes.try_into().unwrap()),
)),
2 => Ok(Instruction::FAdd(
f32::from_le_bytes(a_bytes.try_into().unwrap()),
f32::from_le_bytes(b_bytes.try_into().unwrap()),
)),
3 => Ok(Instruction::FMul(
f32::from_le_bytes(a_bytes.try_into().unwrap()),
f32::from_le_bytes(b_bytes.try_into().unwrap()),
)),
_ => Err(TryFromInstructionError::InvalidOpcode(opcode)),
}
}
}
// 1.2.8. From<Instruction> for [u8; 9]
impl From<Instruction> for [u8; 9] {
fn from(inst: Instruction) -> Self {
let mut res = [0u8; 9];
match inst {
Instruction::Add(a, b) => {
res[0] = 0;
res[1..5].copy_from_slice(&a.to_le_bytes());
res[5..9].copy_from_slice(&b.to_le_bytes());
}
Instruction::Mul(a, b) => {
res[0] = 1;
res[1..5].copy_from_slice(&a.to_le_bytes());
res[5..9].copy_from_slice(&b.to_le_bytes());
}
Instruction::FAdd(a, b) => {
res[0] = 2;
res[1..5].copy_from_slice(&a.to_le_bytes());
res[5..9].copy_from_slice(&b.to_le_bytes());
}
Instruction::FMul(a, b) => {
res[0] = 3;
res[1..5].copy_from_slice(&a.to_le_bytes());
res[5..9].copy_from_slice(&b.to_le_bytes());
}
}
res
}
}
// =======================================================
fn main() {
// 1.1.2. A first encoder
/*let t = ToUpper {};
println!("{}", t.encode("helLO WOrld"));
let t = ToLower {};
println!("{}", t.encode("helLO WOrld"));*/
// 1.1.3. Two traits for one type
/*let c = Caesar { shift: 13 };
let encoded = c.encode("Hello world~❤️");
let decoded = c.decode(&encoded);
// Print encoded as escaped ascii because several valid ascii are not printable
println!(
"{decoded} <-> {}",
encoded.as_bytes().escape_ascii().to_string()
);*/
// 1.1.4. Implement a trait for a foreign type
let encoded = "Hello world~❤️".encode(13);
let decoded = encoded.as_str().decode(13);
// Print encoded as escaped ascii because several valid ascii are not printable
println!(
"{decoded} <-> {}",
encoded.as_bytes().escape_ascii().to_string()
);
}
// ====================== Tests ======================
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clone() {
let a = Instruction::Add(2, 3);
let b = a.clone();
assert!(matches!(b, Instruction::Add(2, 3)));
}
#[test]
fn copy() {
let a = Instruction::Add(2, 3);
let b = a;
let c = a;
assert!(matches!(b, Instruction::Add(2, 3)));
assert!(matches!(c, Instruction::Add(2, 3)));
}
#[test]
fn debug() {
let a = Instruction::Mul(3, 4);
let s = format!("{a:?}");
assert_eq!(s, "Mul(3, 4)");
}
#[test]
fn partialeq() {
let a = Instruction::FMul(3.0, 4.0);
let b = Instruction::FMul(3.0, 4.0);
assert_eq!(a, b);
}
#[test]
fn display() {
let a = Instruction::Add(1, 2);
let s = format!("{a}");
assert_eq!(s, "1 + 2");
let a = Instruction::Mul(1, 2);
let s = format!("{a}");
assert_eq!(s, "1 * 2");
let a = Instruction::FAdd(1.0, 2.1);
let s = format!("{a}");
assert_eq!(s, "1 + 2.1");
let a = Instruction::FMul(1.1, 2.0);
let s = format!("{a}");
assert_eq!(s, "1.1 * 2");
}
#[test]
fn from_str() {
let a: Instruction = "1 + 2".parse().unwrap();
assert_eq!(a, Instruction::Add(1, 2));
let a: Instruction = "1.0 + 2".parse().unwrap();
assert_eq!(a, Instruction::FAdd(1.0, 2.0));
let a: Instruction = "1 * 2".parse().unwrap();
assert_eq!(a, Instruction::Mul(1, 2));
let a: Instruction = "1 * 2.3".parse().unwrap();
assert_eq!(a, Instruction::FMul(1.0, 2.3));
}
#[test]
fn try_from_u8() {
let data = [0u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d, 00];
let a: Instruction = data[..].try_into().unwrap();
assert_eq!(a, Instruction::Add(1349, 908654));
let data = [1u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d, 00];
let a: Instruction = data[..].try_into().unwrap();
assert_eq!(a, Instruction::Mul(1349, 908654));
let data = [2u8, 0, 0, 0, 64, 0, 0, 64, 64];
let a: Instruction = data[..].try_into().unwrap();
assert_eq!(a, Instruction::FAdd(2.0, 3.0));
let data = [3u8, 0, 0, 0, 64, 0, 0, 64, 64];
let a: Instruction = data[..].try_into().unwrap();
assert_eq!(a, Instruction::FMul(2.0, 3.0));
// If slice is too long, it should not be a problem
let data = [
0u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d, 00, 10, 12, 13, 14,
];
let a: Instruction = data[..].try_into().unwrap();
assert_eq!(a, Instruction::Add(1349, 908654));
}
#[test]
#[should_panic]
fn try_from_small_slice_should_panic() {
let data = [0u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d];
let _: Instruction = data[..].try_into().unwrap();
}
#[test]
#[should_panic]
fn try_from_unknown_opcode_should_panic() {
let data = [5u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d];
let _: Instruction = data[..].try_into().unwrap();
}
#[test]
fn from_intruction() {
let a = Instruction::Add(1349, 908654);
let b: [u8; 9] = a.into();
assert_eq!(b, [0u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d, 00]);
let a = Instruction::Mul(1349, 908654);
let b: [u8; 9] = a.into();
assert_eq!(b, [1u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d, 00]);
let a = Instruction::FAdd(2.0, 3.0);
let b: [u8; 9] = a.into();
assert_eq!(b, [2u8, 0, 0, 0, 64, 0, 0, 64, 64]);
let a = Instruction::FMul(2.0, 3.0);
let b: [u8; 9] = a.into();
assert_eq!(b, [3u8, 0, 0, 0, 64, 0, 0, 64, 64]);
}
}
// ====================== 1. Traits ======================
// 1.1. Creating traits
// 1.1.1. A simple encoding/decoding trait
trait Encoder {
fn encode(&self, message: &str) -> String;
}
trait Decoder {
fn decode(&self, message: &str) -> String;
}
// 1.1.2. A first encoder
struct ToUpper {}
struct ToLower {}
impl Encoder for ToUpper {
fn encode(&self, message: &str) -> String {
message.to_uppercase()
}
}
impl Encoder for ToLower {
fn encode(&self, message: &str) -> String {
message.to_lowercase()
}
}
// 1.1.3. Two traits for one type
struct Caesar {
shift: u8,
}
impl Encoder for Caesar {
fn encode(&self, message: &str) -> String {
message
.chars()
.map(|c| {
if c.is_ascii() {
let shifted = (c as u8 + self.shift) % 128;
shifted as char
} else {
c
}
})
.collect()
}
}
impl Decoder for Caesar {
fn decode(&self, message: &str) -> String {
message
.chars()
.map(|c| {
if c.is_ascii() {
let shifted = (c as u8 + 128 - self.shift) % 128;
shifted as char
} else {
c
}
})
.collect()
}
}
// 1.1.4. Implement a trait for a foreign type
trait CaesarExt {
/// Encode self using a Caesar's cypher with the given shift
fn encode(&self, shift: u8) -> String;
/// Decode self using a Caesar's cypher with the given shift
fn decode(&self, shift: u8) -> String;
}
impl CaesarExt for str {
fn encode(&self, shift: u8) -> String {
let caesar = Caesar { shift };
caesar.encode(self)
}
fn decode(&self, shift: u8) -> String {
let caesar = Caesar { shift };
caesar.decode(self)
}
}
// 1.2. Implementing common traits
// 1.2.1. à 1.2.4. Clone/Copy/Debug/PartialEq
/// An enumeration that represents an instruction to execute
#[derive(Clone, Copy, Debug, PartialEq)]
enum Instruction {
/// Integer addition
Add(i32, i32),
/// Integer multiplication
Mul(i32, i32),
/// Floating point addition
FAdd(f32, f32),
/// Floating point multiplication
FMul(f32, f32),
}
// 1.2.5. Display
impl std::fmt::Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Instruction::Add(a, b) => write!(f, "{} + {}", a, b),
Instruction::Mul(a, b) => write!(f, "{} * {}", a, b),
Instruction::FAdd(a, b) => write!(f, "{} + {}", a, b),
Instruction::FMul(a, b) => write!(f, "{} * {}", a, b),
}
}
}
// 1.2.6. FromStr
#[derive(Debug, PartialEq)]
enum InstructionError {
MalformedExpression,
UnknownOperator,
ParseOperandError(String),
}
impl std::str::FromStr for Instruction {
type Err = InstructionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split_whitespace().collect();
if parts.len() != 3 {
return Err(InstructionError::MalformedExpression);
}
let op = parts[1];
let (a_str, b_str) = (parts[0], parts[2]);
let is_float = a_str.contains('.') || b_str.contains('.');
if is_float {
let a = a_str.parse().map_err(|e: std::num::ParseFloatError| {
InstructionError::ParseOperandError(e.to_string())
})?;
let b = b_str.parse().map_err(|e: std::num::ParseFloatError| {
InstructionError::ParseOperandError(e.to_string())
})?;
match op {
"+" => Ok(Instruction::FAdd(a, b)),
"*" => Ok(Instruction::FMul(a, b)),
_ => Err(InstructionError::UnknownOperator),
}
} else {
let a = a_str.parse().map_err(|e: std::num::ParseIntError| {
InstructionError::ParseOperandError(e.to_string())
})?;
let b = b_str.parse().map_err(|e: std::num::ParseIntError| {
InstructionError::ParseOperandError(e.to_string())
})?;
match op {
"+" => Ok(Instruction::Add(a, b)),
"*" => Ok(Instruction::Mul(a, b)),
_ => Err(InstructionError::UnknownOperator),
}
}
}
}
// 1.2.7. TryFrom<&[u8]> for Instruction
#[derive(Debug)]
enum TryFromInstructionError {
InvalidLength,
InvalidOpcode(u8),
}
impl TryFrom<&[u8]> for Instruction {
type Error = TryFromInstructionError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
if value.len() < 9 {
return Err(TryFromInstructionError::InvalidLength);
}
let opcode = value[0];
let (a_bytes, b_bytes) = (&value[1..5], &value[5..9]);
match opcode {
0 => Ok(Instruction::Add(
i32::from_le_bytes(a_bytes.try_into().unwrap()),
i32::from_le_bytes(b_bytes.try_into().unwrap()),
)),
1 => Ok(Instruction::Mul(
i32::from_le_bytes(a_bytes.try_into().unwrap()),
i32::from_le_bytes(b_bytes.try_into().unwrap()),
)),
2 => Ok(Instruction::FAdd(
f32::from_le_bytes(a_bytes.try_into().unwrap()),
f32::from_le_bytes(b_bytes.try_into().unwrap()),
)),
3 => Ok(Instruction::FMul(
f32::from_le_bytes(a_bytes.try_into().unwrap()),
f32::from_le_bytes(b_bytes.try_into().unwrap()),
)),
_ => Err(TryFromInstructionError::InvalidOpcode(opcode)),
}
}
}
// 1.2.8. From<Instruction> for [u8; 9]
impl From<Instruction> for [u8; 9] {
fn from(inst: Instruction) -> Self {
let mut res = [0u8; 9];
match inst {
Instruction::Add(a, b) => {
res[0] = 0;
res[1..5].copy_from_slice(&a.to_le_bytes());
res[5..9].copy_from_slice(&b.to_le_bytes());
}
Instruction::Mul(a, b) => {
res[0] = 1;
res[1..5].copy_from_slice(&a.to_le_bytes());
res[5..9].copy_from_slice(&b.to_le_bytes());
}
Instruction::FAdd(a, b) => {
res[0] = 2;
res[1..5].copy_from_slice(&a.to_le_bytes());
res[5..9].copy_from_slice(&b.to_le_bytes());
}
Instruction::FMul(a, b) => {
res[0] = 3;
res[1..5].copy_from_slice(&a.to_le_bytes());
res[5..9].copy_from_slice(&b.to_le_bytes());
}
}
res
}
}
// ========================================================
// ====================== 2. Generic ======================
// 2.1. Without trait bounds
enum Either<L, R> {
Left(L),
Right(R),
}
impl<L, R> Either<L, R> {
/// Consume self and return option that contains the left variant if any
fn left(self) -> Option<L> {
match self {
Either::Left(l) => Some(l),
Either::Right(_) => None,
}
}
/// Consume self and return option that contains the right variant if any
fn right(self) -> Option<R> {
match self {
Either::Right(r) => Some(r),
Either::Left(_) => None,
}
}
/// Returns true if self is the left variant
fn is_left(&self) -> bool {
match self {
Either::Left(_) => true,
Either::Right(_) => false,
}
}
/// Returns true if self is the right variant
fn is_right(&self) -> bool {
match self {
Either::Right(_) => true,
Either::Left(_) => false,
}
}
/// Returns a new Either that that holds reference to this either inner value
fn as_ref(&self) -> Either<&L, &R> {
match self {
Either::Left(l) => Either::Left(l),
Either::Right(r) => Either::Right(r),
}
}
}
// 2.2. With trait bounds
use std::io::{BufRead, BufReader, BufWriter, Read, Write};
struct IOEncoder<E, I, O> {
encoder: E,
input: I,
output: O,
}
impl<E, I, O> IOEncoder<E, I, O>
where
E: Encoder + Decoder,
I: Read,
O: Write,
{
/// Creates a new IOEncoder
fn new(encoder: E, input: I, output: O) -> Self {
Self {
encoder,
input,
output,
}
}
/// Process this encoders input by encoding each line of the input and saving the encoded version to
/// the ouput
fn encode(&mut self) -> Result<(), std::io::Error> {
let reader = BufReader::new(&mut self.input);
let mut writer = BufWriter::new(&mut self.output);
for line in reader.lines() {
let line = line?;
let encoded = self.encoder.encode(&line);
writeln!(writer, "{}", encoded)?;
}
Ok(())
}
/// Process this encoders input by decoding each line of the input and saving the decoded version to
/// the ouput
fn decode(&mut self) -> Result<(), std::io::Error> {
let reader = BufReader::new(&mut self.input);
let mut writer = BufWriter::new(&mut self.output);
for line in reader.lines() {
let line = line?;
let decoded = self.encoder.decode(&line);
writeln!(writer, "{}", decoded)?;
}
Ok(())
}
}
// =========================================================
// =============== 2. Long exercise proposal ===============
// =========================================================
fn main() {
// 1.1.2. A first encoder
/*let t = ToUpper {};
println!("{}", t.encode("helLO WOrld"));
let t = ToLower {};
println!("{}", t.encode("helLO WOrld"));*/
// 1.1.3. Two traits for one type
/*let c = Caesar { shift: 13 };
let encoded = c.encode("Hello world~❤️");
let decoded = c.decode(&encoded);
// Print encoded as escaped ascii because several valid ascii are not printable
println!(
"{decoded} <-> {}",
encoded.as_bytes().escape_ascii().to_string()
);*/
// 1.1.4. Implement a trait for a foreign type
/*let encoded = "Hello world~❤️".encode(13);
let decoded = encoded.as_str().decode(13);
// Print encoded as escaped ascii because several valid ascii are not printable
println!(
"{decoded} <-> {}",
encoded.as_bytes().escape_ascii().to_string()
);*/
// 2.1. Without trait bounds
let want_a_string = true;
let want_b_string = !want_a_string;
let a = if want_a_string {
Either::Left("A")
} else {
Either::Right(42)
};
let b = if want_b_string {
Either::Left("B")
} else {
Either::Right(54)
};
if a.is_left() {
println!("It is a string: {}", a.left().unwrap());
} else {
println!("It is a number: {}", a.right().unwrap());
}
println!("{}", b.as_ref().right().unwrap());
println!("{}", b.left().unwrap_or("this is no string :/"));
}
// ====================== Tests ======================
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clone() {
let a = Instruction::Add(2, 3);
let b = a.clone();
assert!(matches!(b, Instruction::Add(2, 3)));
}
#[test]
fn copy() {
let a = Instruction::Add(2, 3);
let b = a;
let c = a;
assert!(matches!(b, Instruction::Add(2, 3)));
assert!(matches!(c, Instruction::Add(2, 3)));
}
#[test]
fn debug() {
let a = Instruction::Mul(3, 4);
let s = format!("{a:?}");
assert_eq!(s, "Mul(3, 4)");
}
#[test]
fn partialeq() {
let a = Instruction::FMul(3.0, 4.0);
let b = Instruction::FMul(3.0, 4.0);
assert_eq!(a, b);
}
#[test]
fn display() {
let a = Instruction::Add(1, 2);
let s = format!("{a}");
assert_eq!(s, "1 + 2");
let a = Instruction::Mul(1, 2);
let s = format!("{a}");
assert_eq!(s, "1 * 2");
let a = Instruction::FAdd(1.0, 2.1);
let s = format!("{a}");
assert_eq!(s, "1 + 2.1");
let a = Instruction::FMul(1.1, 2.0);
let s = format!("{a}");
assert_eq!(s, "1.1 * 2");
}
#[test]
fn from_str() {
let a: Instruction = "1 + 2".parse().unwrap();
assert_eq!(a, Instruction::Add(1, 2));
let a: Instruction = "1.0 + 2".parse().unwrap();
assert_eq!(a, Instruction::FAdd(1.0, 2.0));
let a: Instruction = "1 * 2".parse().unwrap();
assert_eq!(a, Instruction::Mul(1, 2));
let a: Instruction = "1 * 2.3".parse().unwrap();
assert_eq!(a, Instruction::FMul(1.0, 2.3));
}
#[test]
fn try_from_u8() {
let data = [0u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d, 00];
let a: Instruction = data[..].try_into().unwrap();
assert_eq!(a, Instruction::Add(1349, 908654));
let data = [1u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d, 00];
let a: Instruction = data[..].try_into().unwrap();
assert_eq!(a, Instruction::Mul(1349, 908654));
let data = [2u8, 0, 0, 0, 64, 0, 0, 64, 64];
let a: Instruction = data[..].try_into().unwrap();
assert_eq!(a, Instruction::FAdd(2.0, 3.0));
let data = [3u8, 0, 0, 0, 64, 0, 0, 64, 64];
let a: Instruction = data[..].try_into().unwrap();
assert_eq!(a, Instruction::FMul(2.0, 3.0));
// If slice is too long, it should not be a problem
let data = [
0u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d, 00, 10, 12, 13, 14,
];
let a: Instruction = data[..].try_into().unwrap();
assert_eq!(a, Instruction::Add(1349, 908654));
}
#[test]
#[should_panic]
fn try_from_small_slice_should_panic() {
let data = [0u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d];
let _: Instruction = data[..].try_into().unwrap();
}
#[test]
#[should_panic]
fn try_from_unknown_opcode_should_panic() {
let data = [5u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d];
let _: Instruction = data[..].try_into().unwrap();
}
#[test]
fn from_intruction() {
let a = Instruction::Add(1349, 908654);
let b: [u8; 9] = a.into();
assert_eq!(b, [0u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d, 00]);
let a = Instruction::Mul(1349, 908654);
let b: [u8; 9] = a.into();
assert_eq!(b, [1u8, 0x45, 0x05, 00, 00, 0x6e, 0xdd, 0x0d, 00]);
let a = Instruction::FAdd(2.0, 3.0);
let b: [u8; 9] = a.into();
assert_eq!(b, [2u8, 0, 0, 0, 64, 0, 0, 64, 64]);
let a = Instruction::FMul(2.0, 3.0);
let b: [u8; 9] = a.into();
assert_eq!(b, [3u8, 0, 0, 0, 64, 0, 0, 64, 64]);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment