1
0
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:
Martin Geisler
2022-12-21 16:36:30 +01:00
commit c212a473ba
252 changed files with 8047 additions and 0 deletions

View File

@ -0,0 +1,3 @@
# Day 2: Afternoon Exercises
The exercises for this afternoon will focus on strings and iterators.

View 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);
}
```

View 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() {}
```

View 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

View 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.

View 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() {}
```

View 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

View 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}}
```

View 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() {}
```

View 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}}
```

View 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