You've already forked comprehensive-rust
mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-06-29 03:51:35 +02:00
Publish Comprehensive Rust 🦀
This commit is contained in:
3
src/exercises/day-2/afternoon.md
Normal file
3
src/exercises/day-2/afternoon.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Day 2: Afternoon Exercises
|
||||
|
||||
The exercises for this afternoon will focus on strings and iterators.
|
32
src/exercises/day-2/health-statistics.md
Normal file
32
src/exercises/day-2/health-statistics.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Health Statistics
|
||||
|
||||
{{#include ../../../third_party/rust-on-exercism/health-statistics.md}}
|
||||
|
||||
Copy the code below to <https://play.rust-lang.org/> and fill in the missing
|
||||
methods:
|
||||
|
||||
```rust,should_panic
|
||||
// TODO: remove this when you're done with your implementation.
|
||||
#![allow(unused_variables, dead_code)]
|
||||
|
||||
{{#include ../../../third_party/rust-on-exercism/health-statistics.rs}}
|
||||
|
||||
fn main() {
|
||||
let bob = User::new(String::from("Bob"), 32, 155.2);
|
||||
println!("I'm {} and my age is {}", bob.name(), bob.age());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_weight() {
|
||||
let bob = User::new(String::from("Bob"), 32, 155.2);
|
||||
assert_eq!(bob.weight(), 155.2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_age() {
|
||||
let mut bob = User::new(String::from("Bob"), 32, 155.2);
|
||||
assert_eq!(bob.age(), 32);
|
||||
bob.set_age(33);
|
||||
assert_eq!(bob.age(), 33);
|
||||
}
|
||||
```
|
35
src/exercises/day-2/luhn.md
Normal file
35
src/exercises/day-2/luhn.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Luhn Algorithm
|
||||
|
||||
The [Luhn algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) is used to
|
||||
validate credit card numbers. The algorithm takes a string as input and does the
|
||||
following to validate the credit card number:
|
||||
|
||||
* Ignore all spaces. Reject number with less than two digits.
|
||||
|
||||
* Moving from right to left, double every second digit: for the number `1234`,
|
||||
we double `3` and `1`.
|
||||
|
||||
* After doubling a digit, sum the digits. So doubling `7` becomes `14` which
|
||||
becomes `5`.
|
||||
|
||||
* Sum all the undoubled and doubled digits.
|
||||
|
||||
* The credit card number is valid if the sum is ends with `0`.
|
||||
|
||||
Copy the following code to <https://play.rust-lang.org/> and implement the
|
||||
function:
|
||||
|
||||
|
||||
```rust
|
||||
// TODO: remove this when you're done with your implementation.
|
||||
#![allow(unused_variables, dead_code)]
|
||||
|
||||
{{#include luhn.rs:luhn}}
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
{{#include luhn.rs:unit-tests}}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn main() {}
|
||||
```
|
88
src/exercises/day-2/luhn.rs
Normal file
88
src/exercises/day-2/luhn.rs
Normal file
@ -0,0 +1,88 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// ANCHOR: luhn
|
||||
pub fn luhn(cc_number: &str) -> bool {
|
||||
// ANCHOR_END: luhn
|
||||
let mut digits_seen = 0;
|
||||
let mut sum = 0;
|
||||
for (i, ch) in cc_number.chars().rev().filter(|&ch| ch != ' ').enumerate() {
|
||||
match ch.to_digit(10) {
|
||||
Some(d) => {
|
||||
sum += if i % 2 == 1 {
|
||||
let dd = d * 2;
|
||||
dd / 10 + dd % 10
|
||||
} else {
|
||||
d
|
||||
};
|
||||
digits_seen += 1;
|
||||
}
|
||||
None => return false,
|
||||
}
|
||||
}
|
||||
|
||||
if digits_seen < 2 {
|
||||
return false;
|
||||
}
|
||||
|
||||
sum % 10 == 0
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cc_number = "1234 5678 1234 5670";
|
||||
println!(
|
||||
"Is {} a valid credit card number? {}",
|
||||
cc_number,
|
||||
if luhn(cc_number) { "yes" } else { "no" }
|
||||
);
|
||||
}
|
||||
|
||||
// ANCHOR: unit-tests
|
||||
#[test]
|
||||
fn test_non_digit_cc_number() {
|
||||
assert!(!luhn("foo"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_cc_number() {
|
||||
assert!(!luhn(""));
|
||||
assert!(!luhn(" "));
|
||||
assert!(!luhn(" "));
|
||||
assert!(!luhn(" "));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_digit_cc_number() {
|
||||
assert!(!luhn("0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_two_digit_cc_number() {
|
||||
assert!(luhn(" 0 0 "));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_valid_cc_number() {
|
||||
assert!(luhn("4263 9826 4026 9299"));
|
||||
assert!(luhn("4539 3195 0343 6467"));
|
||||
assert!(luhn("7992 7398 713"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_cc_number() {
|
||||
assert!(!luhn("4223 9826 4026 9299"));
|
||||
assert!(!luhn("4539 3195 0343 6476"));
|
||||
assert!(!luhn("8273 1232 7352 0569"));
|
||||
}
|
||||
// ANCHOR_END: unit-tests
|
7
src/exercises/day-2/morning.md
Normal file
7
src/exercises/day-2/morning.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Day 2: Morning Exercises
|
||||
|
||||
We will look at implementing methods in two contexts:
|
||||
|
||||
* Simple struct which tracks health statistics.
|
||||
|
||||
* Multiple structs and enums for a drawing library.
|
41
src/exercises/day-2/points-polygons.md
Normal file
41
src/exercises/day-2/points-polygons.md
Normal file
@ -0,0 +1,41 @@
|
||||
# Polygon Struct
|
||||
|
||||
We will create a `Polygon` struct which contain some points. Copy the code below
|
||||
to <https://play.rust-lang.org/> and fill in the missing methods to make the
|
||||
tests pass:
|
||||
|
||||
```rust
|
||||
// TODO: remove this when you're done with your implementation.
|
||||
#![allow(unused_variables, dead_code)]
|
||||
|
||||
{{#include points-polygons.rs:Point}}
|
||||
// add fields
|
||||
}
|
||||
|
||||
{{#include points-polygons.rs:Point-impl}}
|
||||
// add methods
|
||||
}
|
||||
|
||||
{{#include points-polygons.rs:Polygon}}
|
||||
// add fields
|
||||
}
|
||||
|
||||
{{#include points-polygons.rs:Polygon-impl}}
|
||||
// add methods
|
||||
}
|
||||
|
||||
{{#include points-polygons.rs:Circle}}
|
||||
// add fields
|
||||
}
|
||||
|
||||
{{#include points-polygons.rs:Circle-impl}}
|
||||
// add methods
|
||||
}
|
||||
|
||||
{{#include points-polygons.rs:Shape}}
|
||||
|
||||
{{#include points-polygons.rs:unit-tests}}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn main() {}
|
||||
```
|
223
src/exercises/day-2/points-polygons.rs
Normal file
223
src/exercises/day-2/points-polygons.rs
Normal file
@ -0,0 +1,223 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
// ANCHOR: Point
|
||||
pub struct Point {
|
||||
// ANCHOR_END: Point
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
// ANCHOR: Point-impl
|
||||
impl Point {
|
||||
// ANCHOR_END: Point-impl
|
||||
pub fn new(x: i32, y: i32) -> Point {
|
||||
Point { x, y }
|
||||
}
|
||||
|
||||
pub fn magnitude(self) -> f64 {
|
||||
f64::from(self.x.pow(2) + self.y.pow(2)).sqrt()
|
||||
}
|
||||
|
||||
pub fn dist(self, other: Point) -> f64 {
|
||||
(self - other).magnitude()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add for Point {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: Self) -> Self::Output {
|
||||
Self {
|
||||
x: self.x + other.x,
|
||||
y: self.y + other.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Sub for Point {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, other: Self) -> Self::Output {
|
||||
Self {
|
||||
x: self.x - other.x,
|
||||
y: self.y - other.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ANCHOR: Polygon
|
||||
pub struct Polygon {
|
||||
// ANCHOR_END: Polygon
|
||||
points: Vec<Point>,
|
||||
}
|
||||
|
||||
// ANCHOR: Polygon-impl
|
||||
impl Polygon {
|
||||
// ANCHOR_END: Polygon-impl
|
||||
pub fn new() -> Polygon {
|
||||
Polygon { points: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn add_point(&mut self, point: Point) {
|
||||
self.points.push(point);
|
||||
}
|
||||
|
||||
pub fn left_most_point(&self) -> Option<Point> {
|
||||
self.points.iter().min_by_key(|p| p.x).copied()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Point> {
|
||||
self.points.iter()
|
||||
}
|
||||
|
||||
pub fn length(&self) -> f64 {
|
||||
if self.points.is_empty() {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let mut result = 0.0;
|
||||
let mut last_point = self.points[0];
|
||||
for point in &self.points[1..] {
|
||||
result += last_point.dist(*point);
|
||||
last_point = *point;
|
||||
}
|
||||
result += last_point.dist(self.points[0]);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
// ANCHOR: Circle
|
||||
pub struct Circle {
|
||||
// ANCHOR_END: Circle
|
||||
center: Point,
|
||||
radius: i32,
|
||||
}
|
||||
|
||||
// ANCHOR: Circle-impl
|
||||
impl Circle {
|
||||
// ANCHOR_END: Circle-impl
|
||||
pub fn new(center: Point, radius: i32) -> Circle {
|
||||
Circle { center, radius }
|
||||
}
|
||||
|
||||
pub fn circumference(&self) -> f64 {
|
||||
2.0 * std::f64::consts::PI * f64::from(self.radius)
|
||||
}
|
||||
|
||||
pub fn dist(&self, other: &Self) -> f64 {
|
||||
self.center.dist(other.center)
|
||||
}
|
||||
}
|
||||
|
||||
// ANCHOR: Shape
|
||||
pub enum Shape {
|
||||
Polygon(Polygon),
|
||||
Circle(Circle),
|
||||
}
|
||||
// ANCHOR_END: Shape
|
||||
|
||||
impl From<Polygon> for Shape {
|
||||
fn from(poly: Polygon) -> Self {
|
||||
Shape::Polygon(poly)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Circle> for Shape {
|
||||
fn from(circle: Circle) -> Self {
|
||||
Shape::Circle(circle)
|
||||
}
|
||||
}
|
||||
|
||||
impl Shape {
|
||||
pub fn circumference(&self) -> f64 {
|
||||
match self {
|
||||
Shape::Polygon(poly) => poly.length(),
|
||||
Shape::Circle(circle) => circle.circumference(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ANCHOR: unit-tests
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn round_two_digits(x: f64) -> f64 {
|
||||
(x * 100.0).round() / 100.0
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_point_magnitude() {
|
||||
let p1 = Point::new(12, 13);
|
||||
assert_eq!(round_two_digits(p1.magnitude()), 17.69);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_point_dist() {
|
||||
let p1 = Point::new(10, 10);
|
||||
let p2 = Point::new(14, 13);
|
||||
assert_eq!(round_two_digits(p1.dist(p2)), 5.00);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_point_add() {
|
||||
let p1 = Point::new(16, 16);
|
||||
let p2 = p1 + Point::new(-4, 3);
|
||||
assert_eq!(p2, Point::new(12, 19));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_polygon_left_most_point() {
|
||||
let p1 = Point::new(12, 13);
|
||||
let p2 = Point::new(16, 16);
|
||||
|
||||
let mut poly = Polygon::new();
|
||||
poly.add_point(p1);
|
||||
poly.add_point(p2);
|
||||
assert_eq!(poly.left_most_point(), Some(p1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_polygon_iter() {
|
||||
let p1 = Point::new(12, 13);
|
||||
let p2 = Point::new(16, 16);
|
||||
|
||||
let mut poly = Polygon::new();
|
||||
poly.add_point(p1);
|
||||
poly.add_point(p2);
|
||||
|
||||
let points = poly.iter().cloned().collect::<Vec<_>>();
|
||||
assert_eq!(points, vec![Point::new(12, 13), Point::new(16, 16)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shape_circumferences() {
|
||||
let mut poly = Polygon::new();
|
||||
poly.add_point(Point::new(12, 13));
|
||||
poly.add_point(Point::new(16, 16));
|
||||
let shapes = vec![
|
||||
Shape::from(poly),
|
||||
Shape::from(Circle::new(Point::new(10, 20), 5)),
|
||||
];
|
||||
let circumferences = shapes
|
||||
.iter()
|
||||
.map(Shape::circumference)
|
||||
.map(round_two_digits)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(circumferences, vec![10.0, 31.42]);
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: unit-tests
|
17
src/exercises/day-2/solutions-afternoon.md
Normal file
17
src/exercises/day-2/solutions-afternoon.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Day 2 Afternoon Exercises
|
||||
|
||||
## Luhn Algorithm
|
||||
|
||||
([back to exercise](luhn.md))
|
||||
|
||||
```rust
|
||||
{{#include luhn.rs}}
|
||||
```
|
||||
|
||||
## Strings and Iterators
|
||||
|
||||
([back to exercise](strings-iterators.md))
|
||||
|
||||
```rust
|
||||
{{#include strings-iterators.rs}}
|
||||
```
|
12
src/exercises/day-2/solutions-morning.md
Normal file
12
src/exercises/day-2/solutions-morning.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Day 2 Morning Exercises
|
||||
|
||||
## Points and Polygons
|
||||
|
||||
([back to exercise](points-polygons.md))
|
||||
|
||||
```rust
|
||||
{{#include points-polygons.rs}}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn main() {}
|
||||
```
|
21
src/exercises/day-2/strings-iterators.md
Normal file
21
src/exercises/day-2/strings-iterators.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Strings and Iterators
|
||||
|
||||
In this exercise, you are implementing a routing component of a web server. The
|
||||
server is configured with a number of _path prefixes_ which are matched against
|
||||
_request paths_. The path prefixes can contain a wildcard character which
|
||||
matches a full segment. See the unit tests below.
|
||||
|
||||
Copy the following code to <https://play.rust-lang.org/> and make the tests
|
||||
pass. Try avoiding allocating a `Vec` for your intermediate results:
|
||||
|
||||
|
||||
```rust
|
||||
// TODO: remove this when you're done with your implementation.
|
||||
#![allow(unused_variables, dead_code)]
|
||||
|
||||
{{#include strings-iterators.rs:prefix_matches}}
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
{{#include strings-iterators.rs:unit-tests}}
|
||||
```
|
75
src/exercises/day-2/strings-iterators.rs
Normal file
75
src/exercises/day-2/strings-iterators.rs
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// ANCHOR: prefix_matches
|
||||
pub fn prefix_matches(prefix: &str, request_path: &str) -> bool {
|
||||
// ANCHOR_END: prefix_matches
|
||||
let mut prefixes = prefix
|
||||
.split('/')
|
||||
.map(|p| Some(p))
|
||||
.chain(std::iter::once(None));
|
||||
let mut request_paths = request_path
|
||||
.split('/')
|
||||
.map(|p| Some(p))
|
||||
.chain(std::iter::once(None));
|
||||
|
||||
for (prefix, request_path) in prefixes.by_ref().zip(&mut request_paths) {
|
||||
match (prefix, request_path) {
|
||||
(Some(prefix), Some(request_path)) => {
|
||||
if (prefix != "*") && (prefix != request_path) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
(Some(_), None) => return false,
|
||||
(None, None) => break,
|
||||
(None, Some(_)) => break,
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
// ANCHOR: unit-tests
|
||||
#[test]
|
||||
fn test_matches_without_wildcard() {
|
||||
assert!(prefix_matches("/v1/publishers", "/v1/publishers"));
|
||||
assert!(prefix_matches("/v1/publishers", "/v1/publishers/abc-123"));
|
||||
assert!(prefix_matches("/v1/publishers", "/v1/publishers/abc/books"));
|
||||
|
||||
assert!(!prefix_matches("/v1/publishers", "/v1"));
|
||||
assert!(!prefix_matches("/v1/publishers", "/v1/publishersBooks"));
|
||||
assert!(!prefix_matches("/v1/publishers", "/v1/parent/publishers"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matches_with_wildcard() {
|
||||
assert!(prefix_matches(
|
||||
"/v1/publishers/*/books",
|
||||
"/v1/publishers/foo/books"
|
||||
));
|
||||
assert!(prefix_matches(
|
||||
"/v1/publishers/*/books",
|
||||
"/v1/publishers/bar/books"
|
||||
));
|
||||
assert!(prefix_matches(
|
||||
"/v1/publishers/*/books",
|
||||
"/v1/publishers/foo/books/book1"
|
||||
));
|
||||
|
||||
assert!(!prefix_matches("/v1/publishers/*/books", "/v1/publishers"));
|
||||
assert!(!prefix_matches(
|
||||
"/v1/publishers/*/books",
|
||||
"/v1/publishers/foo/booksByAuthor"
|
||||
));
|
||||
}
|
||||
// ANCHOR_END: unit-tests
|
Reference in New Issue
Block a user