🎉 OSS Release! 🚀

Co-authored-by: Joshy Orndorff <admin@joshyorndorff.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
Dan Shields
2023-04-05 23:53:00 -06:00
commit 580687a11e
16 changed files with 2079 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
**/target
.vscode
Generated
+7
View File
@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "pba-qualifier-exam"
version = "1.0.0"
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "pba-qualifier-exam"
version = "1.0.0"
# This exam will be graded by rust stable 1.68 using the 2021 edition.
# Solutions may not require newer editions or versions including nightly.
rust-version = "1.68"
edition = "2021"
# Solutions must not be shared!
publish = false
[dependencies]
# There should be NO external additions here, per the honor code.
# If you want or need to create a local dependancy, you may do so.
+373
View File
@@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
+77
View File
@@ -0,0 +1,77 @@
<h1 align="center">Rust Qualifier Exam</h1>
An open source learning resource, available to all.
This exam is maintained by the Polkadot Blockchain Academy, for the benefit of the entire Rust community.
The Academy accepts individuals modestly skilled in Rust, and maintain this exam to test help everyone asses their proficiency being of a level we would consider for the program.
We encourage everyone to take this exam for fun, to help assess your own Rust understanding, or as part of your [application to the Polkadot Blockchain Academy](#applying-to-the-polkadot-blockchain-academy).
## Honor Code
When used as a qualifier for the Academy, this exam is intended to be taken individually without help from other programmers, AIs, or the breadth of the internet.
The first section of the exam is the honor code.
Please read it _carefully and thoughtfully_ before you review or start any work.
Those not applying to the Academy are free to relax the honor code as you deem fit, however, the **[license and use policy](#license-and-use-policies) forbids publishing solutions** for everyone.
Naturally as an exam, public solutions undermine usefulness of this public good for the whole community.
We humbly ask you respect the intended academic integrity of our community when considering how you interact with the exam in your communities.
## Time Commitment
The exam is intended to be quite a challenge for those more novice in Rust.
It is expected to take a minimum of 10-20 hours for the average applicant to complete.
Your time may be shorter or significantly longer than this.
## Organization
This exam is structured as a single Rust crate with many modules.
Each module contains some well-commented starter code, and some `todo!()` macros that you should replace with solution code.
## Tests
Each module contains a small starter test suite to help you know whether your solutions are on the right track or not.
You can run these tests with standard cargo commands.
The provided tests are mostly failing when you first begin, but they should all pass by the time you are finished.
```sh
# Run the unit tests
cargo test
# Run the doc doc tests
cargo test --doc
```
The provided tests are **not** a complete test suite.
You are encouraged to write additional tests to ensure that their code works as intended.
The Academy maintains a larger more thorough test suite privately.
If you are applying, your submission will be assessed with this more thorough test suite.
To be sure that the test suite will always work, **do not change any function or type signatures** unless explicitly permitted to do so.
The entire exam should be completed using Rust stable toolchain with the specified version in [Cargo.toml](./Cargo.toml) as the Academy test suite will explicitly use this version.
## Completing the Exam
Work through the exam by replacing each occurrence of `todo!()` with your solution to the problem.
When all the provided tests pass, plus all the additional tests you wrote are passing, your work is finished.
If you are pushing to any remote servers while solving the exam (as is required for PBA applicants) be sure that the remote servers are not publicly viewable.
You may not publish solutions to the exam, please read the [License and use policies](#license-and-use-policies) for specifics
## Applying to the Polkadot Blockchain Academy
The Polkadot Blockchain Academy is a classroom-based educational program covering the conceptual underpinnings and the hands-on application of blockchain technology.
The curriculum covers disciplines such as economics, governance, game theory, cryptography, peer-based and distributed network systems, systems and API design, and much more.
In addition to theoretical modules, students will apply their knowledge and build blockchains and parachains using [Substrate](https://substrate.io).
To apply to the Polkadot Blockchain Academy, please visit https://polkadot.network/development/academy/ that has information location and dates of the current or next cohort, and a form that will start the process for consideration in the next cohort.
This qualifier exam is just one part of the process and is not, by itself, an application.
### License and Use Policies
Mozilla Public License Version 2.0 - See the [LICENSE](./LICENSE) for details.
In addition to the license, you may not publish materials that reveal, directly or indirectly, in any way, solutions to this exam publicly.
Lastly, you should not share any solutions or privately to prevent peer-collusion and respect the academic integrity of this public good resource of the Academy's community.
# 🚀 Good luck! 🚀
+7
View File
@@ -0,0 +1,7 @@
hard_tabs = true
newline_style = "Unix"
# Comment formatting requires nightly which is not enforced by CI.
# Nonetheless, we leave the config here so it can be run locally.
comment_width = 100
wrap_comments = true
+90
View File
@@ -0,0 +1,90 @@
//! This portion of the exam represents an honor code. By returning `true` from each of these
//! functions you are attesting that you have followed the various rules of the exam.
//!
//! Cheating on this exam will only hurt yourself as you are likely to feel lost and frustrated at
//! the Polkadot Blockchain Academy if you do not have the necessary Rust background to attend.
//!
//! If you are in any doubt of something being allowed or disallowed not, please directly ask the
//! Academy staff for clarification and guidance.
/// You must work independently on this exam. Seeking help from or otherwise working anyone on your
/// solutions while you are completing it is considered cheating. You are encouraged to ask the
/// Academy staff if something is unclear or you are completely stuck.
pub fn exam_done_independently() -> bool {
// If you have followed this rule, return `true`
todo!()
}
/// The multiple choice portion of the exam must be completed without accessing the internet,
/// books, or any other resources.
pub fn multiple_choice_closed_book() -> bool {
// If you have followed this rule, return `true`
todo!()
}
/// The multiple choice portion of this exam has several questions asking for the output of a
/// particular programs. It also has questions asking what change to a program would fix a
/// particular issue. For this reason, you are not permitted to compile or run the code snippets
/// provided in the multiple choice portion. You may only trace them manually in your head or on
/// paper. Pretend that this portion of the exam is being administered on paper, and no computer is
/// available at all.
pub fn multiple_choice_no_run() -> bool {
// If you have followed this rule, return `true`
todo!()
}
/// The coding portion of the exam allows access to books, and websites such as the Rust book, the
/// standard library reference, and others _explicitly listed_ in the exam prompts themselves.
/// However, you not allowed to look up direct implementation of the specific algorithms we are
/// asking you to write.
///
/// Examples of allowed searches:
/// - How to swap elements of a slice in Rust
/// - What is the bubble sort algorithm
///
/// Examples of searches that are cheating:
/// - Bubble sort implementation in Rust
/// - Bubble sort implementation in C/python/etc
/// - How to {do exact thing the problem asks for} in Rust/C/Python/etc
///
/// If you are in any doubt of something being allowed or disallowed not, please directly ask the
/// Academy staff for clarification and guidance.
pub fn coding_no_copy() -> bool {
// If you have followed this rule, return `true`
todo!()
}
/// You not allowed to use external dependencies from `crates.io` or elsewhere unless
/// explicitly stated in the problem.
pub fn coding_no_external_deps() -> bool {
// If you have followed this rule, return `true`
todo!()
}
/// You are not allowed to use AI assisted coding tools like Github Copilot to complete this exam.
pub fn coding_no_ai_helpers() -> bool {
// If you have followed this rule, return `true`
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
fn has_honor(f: &dyn Fn() -> bool) {
assert!(
f(),
"Thank you for your honesty in letting us know you have not followed the honor code."
)
}
#[test]
fn honor_code_respected() {
has_honor(&multiple_choice_closed_book);
has_honor(&exam_done_independently);
has_honor(&multiple_choice_no_run);
has_honor(&coding_no_copy);
has_honor(&coding_no_external_deps);
has_honor(&coding_no_ai_helpers);
}
}
+367
View File
@@ -0,0 +1,367 @@
//! # Multiple Choice Questions
//!
//! **This portion of the exam is to be completed without accessing the internet, books, the rust
//! compiler, or any other resources.**
//!
//! There are several questions asking for the output of particular programs or asking what change
//! would fix a particular issue. For this reason, you are **not permitted to compile or run the
//! code snippets**. You may only trace them manually in your head or on paper. Pretend that this
//! portion of the exam is being administered on paper, and no computer is available at all.
//!
//! To enable us to auto-grade your answer, you implement the that returns the `char` representing
//! your answer.
//!
//! If you would like a rendered and styled version of these questions simply build an open the Rust
//! Docs for this module, and navigate the the multiple choice question module docs by the command:
//!
//! ```sh
//! cargo doc --open
//! ```
/// ## Question 1
///
/// Consider this Rust code when answering question 1:
///
/// ```notest
/// let x = 4;
/// let y = 5;
/// let z = x - y;
/// let y = 3;
/// ```
///
/// ### Question 1_A
///
/// What type is the variable x?
///
/// - a) usize
/// - b) isize
/// - c) u32
/// - d) i32
/// - e) int
pub fn answer_1_a() -> char {
todo!()
}
/// ### Question 1_B
///
/// Why can the variable `y` be reassigned in the last line when it was not declared as `mut`?
///
/// - a) Mutable variables are the default.
/// - b) Even immutable variables can be re-assigned, only `const`s cannot.
/// - c) It is not reassigned, it is shadowed.
/// - d) Because the original `y` value was moved into z` on line 3.
/// - e) Because it is being borrowed.
pub fn answer_1_b() -> char {
todo!()
}
/// ### Question 1_C
///
/// What is the value of `z` at the end of the program?
///
/// - a) 1
/// - b) -1
/// - c) 4294967295 (u32 max value)
/// - d) 0
/// - e) 511
pub fn answer_1_c() -> char {
todo!()
}
/// ### Question 1_D
///
/// Which of the following is not a valid looping construct in Rust?
///
/// - a) `do {} while x > 0`
/// - b) `for _ in 1..2 {}`
/// - c) `while x > 0 {}`
/// - d) `loop {}`
/// - e) `for _ in vec![1, 2, 3] {}`
pub fn answer_1_d() -> char {
todo!()
}
/// ## Question 2
///
/// Consider this Rust code:
///
/// ```notest
/// /// Check whether a value is between 5 and 10, and return a user-readable
/// /// string explaining the findings
/// fn value_in_range(n: u8) -> String {
/// if n < 5 {
/// let result = String::from("value is too small");
/// }
/// else if n > 10 {
/// let result = String::from("value is too large");
/// }
/// else {
/// let result = String::from("value is just right");
/// }
/// result
/// }
/// ```
///
/// Why does the above program fail to compile?
///
/// - a) It needs a lifetime parameter.
/// - b) Rust does not support the `else if` syntax.
/// - c) The `u8` type is not appropriate here.
/// - d) It has the wrong return type.
/// - e) The variable `result` is out of scope by the end.
pub fn answer_2() -> char {
todo!()
}
/// ## Question 3
///
/// Consider this Rust code when answering question 3:
///
/// ```notest
/// fn mystery(n: u32) -> Vec<u32> {
/// let mut seq = Vec::new();
/// for k in 1..n {
/// seq.push(k * k + 3);
/// }
/// seq
/// }
/// ```
///
/// ### Question 3_A
///
/// What value is returned when calling `mystery(6)`?
///
/// - a) [4, 7, 12, 19, 28]
/// - b) [3, 4, 7, 12, 19]
/// - c) [4, 7, 12, 19, 28, 39]
/// - d) [4, 10, 18, 28, 40]
/// - e) [28, 19, 12, 7, 4]
pub fn answer_3_a() -> char {
todo!()
}
/// ### Question 3_B
///
/// Consider changing the range `1..n`. Which of the following would _not_ cause the loop to execute
/// one additional time:
///
/// - a) `1..=n`
/// - b) `0..n`
/// - c) `1..n+1`
/// - d) `(1..n).step_by(1)`
/// - e) `(1..=n).step_by(1)`
pub fn answer_3_b() -> char {
todo!()
}
/// ## Question 4
///
/// Consider this Rust code when answering question 4:
///
/// ```notest
/// fn sort(items: &mut [u32]);
/// ```
///
/// ### Question 4_A
///
/// The signature above makes sense for a sorting algorithm that:
///
/// - a) Returns a copy of the data in a new Vec in sorted order.
/// - b) Returns a copy of the data in a new slice in sorted order.
/// - c) Returns a new subslice of the same data in sorted order
/// - d) Sorts the data "in place" by moving elements around.
/// - e) Sorts the data and removes duplicate items.
pub fn answer_4_a() -> char {
todo!()
}
/// ### Question 4_B
///
/// How would this signature need to change to sort arbitrary data?
///
/// - a) `fn sort<T>(items: &mut [T])`
/// - b) `fn sort<T: Ord>(items: &mut [T]);`
/// - c) `fn sort<T: PartialOrd>(items: &mut [T]);`
/// - d) `fn sort(items: &mut [T: PartialOrd]);`
/// - e) `fn sort(items: &mut [T: Ord]);`
pub fn answer_4_b() -> char {
todo!()
}
/// ## Question 5
///
/// Which of the following tools is used to install Rust and manage Rust versions and components?
///
/// - a) cargo
/// - b) rustc
/// - c) rustup
/// - d) clippy
/// - e) nvm
pub fn answer_5() -> char {
todo!()
}
/// ## Question 6
///
/// Consider this Rust code:
///
/// ```notest
/// struct Fraction {
/// numerator: u32,
/// denominator: u32,
/// }
///
/// impl From<u32> for Fraction {
/// fn from(n: u32) -> Self {
/// Self {
/// numerator: n,
/// denominator: 1,
/// }
/// }
/// }
///
/// fn main() {
/// let a: u32 = 5;
/// let b: Fraction = a.into();
/// }
/// ```
///
/// Does the above code compile? Why or why not?
///
/// - a) Yes, because in mathematics, any integer can be turned into a fraction.
/// - b) No, because the denominator should be `n` and the numerator `1`.
/// - c) No, because the `.into()` method comes from the `Into` trait which is not implemented.
/// - d) Yes, because the implementation of `From` implies an implementation of `Into`.
/// - e) Yes, because the implementation of `Into` implies an implementation of `From`.
pub fn answer_6() -> char {
todo!()
}
/// ## Question 7
///
/// Consider this Rust code:
///
/// ```notest
/// use std::num::ParseIntError;
///
/// enum OutOfRangeError {
/// TooLarge,
/// TooSmall,
/// NotEvenANumber,
/// }
///
/// impl From<ParseIntError> for OutOfRangeError {
/// fn from(_e: ParseIntError) -> Self {
/// Self::NotEvenANumber
/// }
/// }
///
/// fn string_to_int_in_range(s: String) -> Result<u32, OutOfRangeError> {
/// // Given: The u32::from_str_radix function returns Result<u32, ParseIntError>
/// let n: u32 = u32::from_str_radix(&s,10)?;
///
/// match n {
/// n if n < 5 => Err(OutOfRangeError::TooSmall),
/// n if n > 100 => Err(OutOfRangeError::TooLarge),
/// n => Ok(n),
/// }
/// }
/// ```
///
/// Does this code compile? Why or why not?
///
/// - a) No, because you cannot use `if` as part of a pattern.
/// - b) No, because `from_str_radix` does not return `Err(OutOfRangeError)` in any case, and that
/// is the error type required by `string_to_int_in_range`.
/// - c) Yes, because any error type can be converted to any other error type by the `?` operator.
/// - d) No, because the potential `ParseIntError` is never handled.
/// - e) Yes, because the `?` operator implicitly performs a `.into()` before returning the error.
pub fn answer_7() -> char {
todo!()
}
/// This function is not graded. It is just for collecting feedback.
/// On a scale from 0 - 255, with zero being extremely easy and 255 being extremely hard,
/// how hard did you find this section of the exam.
pub fn how_hard_was_this_section() -> u8 {
todo!()
}
/// This function is not graded. It is just for collecting feedback.
/// How much time (in hours) did you spend on this section of the exam?
pub fn how_many_hours_did_you_spend_on_this_section() -> u8 {
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
fn sanity_check(f: &dyn Fn() -> char) {
assert!(
"abcde".contains(f()),
"{}",
"You have not returned an answer a, b, c, d, or e."
)
}
#[test]
fn answer_1_a_sanity_check() {
sanity_check(&answer_1_a)
}
#[test]
fn answer_1_b_sanity_check() {
sanity_check(&answer_1_b)
}
#[test]
fn answer_1_c_sanity_check() {
sanity_check(&answer_1_c)
}
#[test]
fn answer_1_d_sanity_check() {
sanity_check(&answer_1_d)
}
#[test]
fn answer_2_sanity_check() {
sanity_check(&answer_2)
}
#[test]
fn answer_3_a_sanity_check() {
sanity_check(&answer_3_a)
}
#[test]
fn answer_3_b_sanity_check() {
sanity_check(&answer_3_b)
}
#[test]
fn answer_4_a_sanity_check() {
sanity_check(&answer_4_a)
}
#[test]
fn answer_4_b_sanity_check() {
sanity_check(&answer_4_b)
}
#[test]
fn answer_5_sanity_check() {
sanity_check(&answer_5)
}
#[test]
fn answer_6_sanity_check() {
sanity_check(&answer_6)
}
#[test]
fn answer_7_sanity_check() {
sanity_check(&answer_7)
}
}
+86
View File
@@ -0,0 +1,86 @@
//! Complete the following functions using the pattern matching syntax. That includes the `match`
//! statement of the `matches!()` macro, if you feel like having an "1-liner".
//!
//! You can try and write them imperatively at first as well, but at the end of the day, we want you
//! to write them using the `match!` or `matches!`.
/// Returns true if the last two strings in the vector start with `PBA`.
pub fn match_1(input: Vec<String>) -> bool {
todo!();
}
/// Returns true if the first and last string in the vector start with `PBA`.
pub fn match_2(input: Vec<String>) -> bool {
todo!();
}
/// Returns true if the first item in `input` is true.
pub fn match_3(input: (bool, bool, bool)) -> bool {
todo!();
}
/// Returns true if the input is `Ok(x)` of some even `x`.
pub fn match_4(input: Result<u32, &'static str>) -> bool {
todo!();
}
/// This function is not graded. It is just for collecting feedback.
/// On a scale from 0 - 255, with zero being extremely easy and 255 being extremely hard,
/// how hard did you find this section of the exam.
pub fn how_hard_was_this_section() -> u8 {
todo!()
}
/// This function is not graded. It is just for collecting feedback.
/// How much time (in hours) did you spend on this section of the exam?
pub fn how_many_hours_did_you_spend_on_this_section() -> u8 {
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_match_1_true() {
let strs = vec![
"Hello".to_string(),
"World".to_string(),
"PBA".to_string(),
"PBASDFGR".to_string(),
];
assert!(match_1(strs))
}
#[test]
fn test_match_1_false() {
let strs = vec!["Hello".to_string(), "World".to_string(), "PBA".to_string()];
assert!(!match_1(strs))
}
#[test]
fn test_match_2() {
let strs = vec![
"PBAHello".to_string(),
"World".to_string(),
"PBA".to_string(),
"PBASDFGR".to_string(),
];
assert!(match_2(strs))
}
#[test]
fn test_match_3() {
assert!(match_3((true, false, true)))
}
#[test]
fn test_match_4_true() {
assert!(match_4(Ok(6)))
}
#[test]
fn test_match_4_false() {
assert!(!match_4(Err("bogus")))
}
}
+150
View File
@@ -0,0 +1,150 @@
//! This portion will test your familiarity with some of Rust's common traits and your ability to
//! implement those traits in interesting ways. You need to remove all of the `todo!()`s. Most of
//! them will need to be replaced by some code, but some may simply be deleted.
// NOTE: You will need to `use` something from the standard library to implement `Ord` and
// `PartialOrd` here.
/// A record of an employee at a particular company
#[derive(Debug)]
pub struct Employee {
/// The name the person likes to be called. Doesn't have to be their "passport name"
pub name: String,
/// Amount of experience (in months) the person has working at this company
pub experience: u32,
/// Hourly wage paid to this employee
pub wage: u32,
/// Unique identifier for this employee
pub uid: u32,
}
// We want to consider two employee instances equal iff they have the same `uid`.
impl PartialEq for Employee {
fn eq(&self, other: &Self) -> bool {
todo!("complete the implementation");
}
}
impl Eq for Employee {}
// We want to sort employees. First and foremost, employees are equal if they have the same
// `uid`, as explained above. For employees who are not equal, we sort by the value they
// bring to the company. Value is defined as the quotient of the experience they've acquired
// at the company divided by their wage. Use integer division for the purpose of this calculation.
impl PartialOrd for Employee {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
todo!("complete the implementation");
}
}
impl Ord for Employee {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
todo!("complete the implementation");
}
}
// We want to parse employee information from a string data
// The string data should be comma separated. Here are a few examples:
//
// * "Billy, 4, 5, 345" - Billy has been working here for 4 months and earns 5 token per hour. She
// is employee #345
// * "Jose, 12, 6, 1" - Jose has been working here for 1 year (12 months) and earns 6
// tokens per hour. He is employee #1
//
// Any strings with the wrong number of commas or numbers too big for `u32` should return `Err(_)`
// where the error message may be anything.
impl TryFrom<String> for Employee {
type Error = &'static str;
fn try_from(value: String) -> Result<Self, Self::Error> {
todo!("complete the implementation");
}
}
// We also want to convert employees back into strings in the same format as above.
impl From<Employee> for String {
fn from(e: Employee) -> Self {
todo!("complete the implementation");
}
}
/// This function is not graded. It is just for collecting feedback.
/// On a scale from 0 - 255, with zero being extremely easy and 255 being extremely hard,
/// how hard did you find this section of the exam.
pub fn how_hard_was_this_section() -> u8 {
todo!()
}
/// This function is not graded. It is just for collecting feedback.
/// How much time (in hours) did you spend on this section of the exam?
pub fn how_many_hours_did_you_spend_on_this_section() -> u8 {
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn employee_from_string_success() {
let s = String::from("Billy, 4, 5, 345");
let billy = Employee {
name: String::from("Billy"),
experience: 4,
wage: 5,
uid: 345,
};
assert_eq!(billy, s.try_into().unwrap())
}
#[test]
fn employee_to_string_success() {
let billy = Employee {
name: String::from("Billy"),
experience: 4,
wage: 5,
uid: 345,
};
assert_eq!(String::from("Billy, 4, 5, 345"), String::from(billy))
}
#[test]
fn employee_ord() {
let billy = Employee {
name: String::from("Billy"),
experience: 4,
wage: 5,
uid: 345,
};
let susie = Employee {
name: String::from("Susie"),
experience: 5,
wage: 5,
uid: 347,
};
assert!(susie > billy);
}
#[test]
fn employee_neq() {
let billy = Employee {
name: String::from("Billy"),
experience: 4,
wage: 5,
uid: 345,
};
let susie = Employee {
name: String::from("Susie"),
experience: 5,
wage: 5,
uid: 347,
};
assert!(susie != billy);
}
}
+137
View File
@@ -0,0 +1,137 @@
//! This portion of the exam tests your abilities to work with iterators and their functional-style
//! methods.
//!
//! Throughout this portion of the test, you may refer to <https://doc.rust-lang.org/std/iter/trait.Iterator.html>
//! and other docs about iterators. You may NOT look up specific implementations for these problems
//! in Rust or any other Functional language.
//!
//! If you find that you simply cannot write these methods in the functional style using iterator
//! methods, writing them in the imperative style with loops will still earn partial credit.
/// This function takes an iterator of u32 values, squares each value, and returns the sum of the
/// squares. You may assume that no individual square, nor the entire sum, overflows the u32 type.
pub fn sum_of_squares(vals: impl Iterator<Item = u32>) -> u32 {
todo!()
}
/// This function takes an iterator of i32 values, calculates the absolute value of each, and throws
/// away any values that are greater than 100. The remaining positive values are returned as an
/// iterator of u32s.
pub fn bounded_absolute_values(vals: impl Iterator<Item = i32>) -> impl Iterator<Item = u32> {
// You should remove the following line (and this comment). It is just there because the
// compiler doesn't allow todo!() when the return type is impl Trait
Vec::new().into_iter()
}
// We allow `unused_mut` only so that there is no build warning on the starter code.
// You should remove this line once you have completed the following function
/// This function takes an iterator of u32 values. The first value in the iterator, call it n, is
/// special: it represents the maximum count of the resultant iterator. Once n is known, create an
/// iterator that yields the first n even values from the remainder of the input iterator.
///
/// If the input iterator is empty, return None
/// If there are fewer than n even values left in the input, return as many as possible
#[allow(unused_mut)]
pub fn first_n_even(mut vals: impl Iterator<Item = u32>) -> Option<impl Iterator<Item = u32>> {
// You should remove the following line (and this comment). It is just there because the
// compiler doesn't allow todo!() when the return type is impl Trait
Some(Vec::new().into_iter())
}
/// Return an "infinite" iterator that yields the squares of the whole numbers.
/// For example, the first few values should be 0, 1, 4, 9, 16, 25, ...
///
/// The iterator should be bounded only by the u32 type, not by your code
pub fn square_whole_numbers() -> impl Iterator<Item = u32> {
// You should remove the following line (and this comment). It is just there because the
// compiler doesn't allow todo!() when the return type is impl Trait
Vec::new().into_iter()
}
/// An iterator that generates the Fibonacci sequence.
#[derive(Default)]
pub struct Fibonacci {
/// The most recent value this iterator has yielded
prev: Option<u32>,
/// The second most recent value that this iterator has yielded
prev_prev: Option<u32>,
}
impl Iterator for Fibonacci {
type Item = u32;
fn next(&mut self) -> Option<u32> {
todo!()
}
}
/// This function is not graded. It is just for collecting feedback.
/// On a scale from 0 - 255, with zero being extremely easy and 255 being extremely hard,
/// how hard did you find this section of the exam.
pub fn how_hard_was_this_section() -> u8 {
todo!()
}
/// This function is not graded. It is just for collecting feedback.
/// How much time (in hours) did you spend on this section of the exam?
pub fn how_many_hours_did_you_spend_on_this_section() -> u8 {
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sum_of_squares_1() {
let initial = [1u32, 2, 3].into_iter();
assert_eq!(14, sum_of_squares(initial));
}
#[test]
fn bounded_absolute_values_1() {
let initial = [1, 5, -5, 101, -200, 9, 0].into_iter();
let expected = vec![1u32, 5, 5, 9, 0];
assert_eq!(
expected,
bounded_absolute_values(initial).collect::<Vec<_>>()
);
}
#[test]
fn first_n_even_1() {
let initial = [3u32, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter();
let expected = vec![2u32, 4, 6];
assert_eq!(expected, first_n_even(initial).unwrap().collect::<Vec<_>>());
}
#[test]
fn first_n_even_2() {
let initial = [3u32, 1, 3, 5, 7, 9].into_iter();
assert_eq!(
Vec::<u32>::new(),
first_n_even(initial).unwrap().collect::<Vec<_>>()
);
}
#[test]
fn square_whole_numbers_1() {
assert_eq!(
vec![0, 1, 4, 9, 16, 25],
square_whole_numbers().take(6).collect::<Vec<_>>()
);
}
#[test]
fn fibonacci_1() {
let fib = Fibonacci::default();
let expected = vec![0u32, 1, 1, 2, 3, 5, 8];
assert_eq!(expected, fib.take(7).collect::<Vec<_>>());
}
}
+347
View File
@@ -0,0 +1,347 @@
// First, we are going to introduce some units of energy. For whatever reason, we prefer BTU above
// Joules and Calories, but we want to support all 3 of these in this module. Double check the
// conversion methods, and make sure you fully understand them.
use std::marker::PhantomData;
// You may uncomment and use the following import if you need it. You may also read its
// documentation at https://doc.rust-lang.org/std/cell/struct.RefCell
// use std::cell::RefCell;
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
pub struct Joule(pub u32);
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
pub struct Calorie(pub u32);
pub type BTU = u32;
impl From<Joule> for BTU {
fn from(j: Joule) -> Self {
j.0 / 1055
}
}
impl From<BTU> for Joule {
fn from(b: BTU) -> Self {
Self(b * 1055)
}
}
impl From<Calorie> for BTU {
fn from(c: Calorie) -> Self {
c.0 / 251
}
}
impl From<BTU> for Calorie {
fn from(b: BTU) -> Self {
Calorie(b * 251)
}
}
// Now, we start defining some types of fuel.
/// A technology for storing energy for later consumption.
pub trait Fuel {
/// The output unit of the energy density.
///
/// Think about this: why did we chose this to be an associated type rather than a generic?
type Output: Into<BTU> + From<BTU>;
/// The amount of energy contained in a single unit of fuel.
fn energy_density() -> Self::Output;
}
pub struct Diesel;
impl Fuel for Diesel {
type Output = Joule;
fn energy_density() -> Self::Output {
todo!("100 BTU")
}
}
pub struct LithiumBattery;
impl Fuel for LithiumBattery {
type Output = Calorie;
fn energy_density() -> Self::Output {
todo!("200 BTU")
}
}
pub struct Uranium;
impl Fuel for Uranium {
type Output = Joule;
fn energy_density() -> Self::Output {
todo!("1000 BTU")
}
}
/// A container for any fuel type.
pub struct FuelContainer<F: Fuel> {
/// The amount of fuel.
amount: u32,
/// NOTE: Fuel doesn't really have any methods that require `&self` on it,
/// so any information that we can get, we can get from `F` as **TYPE**, we don't really need
/// to store an instance of `F`, like `fuel: F` as a struct field. But to satisfy the compiler,
/// we must use `F` somewhere.
/// Thus, this is the perfect use case of `PhantomData`.
_marker: PhantomData<F>,
}
impl<F: Fuel> FuelContainer<F> {
pub fn new(amount: u32) -> Self {
Self {
amount,
_marker: Default::default(),
}
}
}
/// Something that can provide energy from a given `F` fuel type, like a power-plant.
pub trait ProvideEnergy<F: Fuel> {
/// Consume the fuel container and return the created energy, based on the power density of the
/// fuel and potentially other factors.
///
/// Some fuel providers might have some kind of decay or inefficiency, which should be reflected
/// here. Otherwise, [ProvideEnergy::provide_energy_with_efficiency] or
/// [ProvideEnergy::provide_energy_ideal] might be good enough.
///
/// Not all `ProvideEnergy` implementations need to have internal state. Therefore, this
/// interface accepts `&self`, not `&mut self`. You might need to use special language features
/// to overcome this.
fn provide_energy(&self, f: FuelContainer<F>) -> <F as Fuel>::Output;
/// Convert the amount of fuel in `f` with an exact efficiency of `e`.
///
/// NOTE: all efficiencies are interpreted as u8 values that can be at most 100, and represent a
/// percent.
///
/// This method must be provided as it will be the same in all implementations.
fn provide_energy_with_efficiency(&self, f: FuelContainer<F>, e: u8) -> <F as Fuel>::Output {
todo!();
}
/// Same as [`ProvideEnergy::provide_energy_with_efficiency`], but with an efficiency of 100.
///
/// This method must be provided as it will be the same in all implementations.
fn provide_energy_ideal(&self, f: FuelContainer<F>) -> <F as Fuel>::Output {
todo!();
}
}
/// A nuclear reactor that can only consume `Uranium` and provide energy with 99% efficiency.
pub struct NuclearReactor;
impl<F: Fuel> ProvideEnergy<F> for NuclearReactor {
fn provide_energy(&self, f: FuelContainer<F>) -> <F as Fuel>::Output {
todo!("complete the implementation; note that you might need to change the trait bounds and generics of the `impl` line");
}
}
/// A combustion engine that can only consume `Diesel`.
///
/// The `DECAY` const must be interpreted as such: per every `DECAY` times `provide_energy` is
/// called on an instance of this type, the efficiency should reduce by one. The initial efficiency
/// must be configurable with a `fn new(efficiency: u8) -> Self`.
pub struct InternalCombustion<const DECAY: u32>(/* Fill the fields as needed */);
impl<const DECAY: u32> InternalCombustion<DECAY> {
pub fn new(efficiency: u8) -> Self {
todo!()
}
}
impl<const DECAY: u32, F: Fuel> ProvideEnergy<F> for InternalCombustion<DECAY> {
fn provide_energy(&self, f: FuelContainer<F>) -> <F as Fuel>::Output {
todo!("complete the implementation; note that you might need to change the trait bounds and generics of the `impl` line");
}
}
/// A hypothetical device that can, unlike the `InternalCombustion`, consume **any fuel** that's of
/// type `trait Fuel`. It can provide a fixed efficiency regardless of fuel type. As before,
/// EFFICIENCY is a u8 whose value should not exceed 100 and is interpreted as a percent.
pub struct OmniGenerator<const EFFICIENCY: u8>;
// NOTE: implement `ProvideEnergy` for `OmniGenerator` using only one `impl` block.
impl<const EFFICIENCY: u8, F: Fuel> ProvideEnergy<F> for OmniGenerator<EFFICIENCY> {
fn provide_energy(&self, f: FuelContainer<F>) -> <F as Fuel>::Output {
todo!("complete the implementation; note that you might need to change the trait bounds and generics of the `impl` line");
}
}
/// A type that can wrap two different fuel types and mix them together.
///
/// The energy density of the new fuel type is the average of the two given, once converted to BTU.
/// The output unit should also be BTU.
///
/// This can represent a new fuel type, thus it must implement `Fuel`.
pub struct Mixed<F1: Fuel, F2: Fuel>(PhantomData<(F1, F2)>);
impl<F1: Fuel, F2: Fuel> Fuel for Mixed<F1, F2> {
type Output = BTU;
fn energy_density() -> Self::Output {
todo!("complete the implementation; note that you might need to change the trait bounds and generics of the `impl` line");
}
}
// Now think about how you can make the mixer configurable, such that it would produce a new fuel
// with an energy density that is more influences by one type than the other.
//
// For example, you have a mixer of F1, F2, and some coefficient C1, where the energy density of the
// mixture is `F1 * C1 + F2 * (1 - C1) )` where `C1` is a ratio (which you have to represent again
// with a u8 percent).
//
// The main trick is to overcome the fact that `fn energy_density` does not take in a `self`, so the
// coefficients need to be incorporated in some other way (you've already seen examples of that in
// this file ;)).
pub struct CustomMixed<const C: u8, F1, F2>(PhantomData<(F1, F2)>);
impl<const C: u8, F1: Fuel, F2: Fuel> Fuel for CustomMixed<C, F1, F2> {
type Output = BTU;
fn energy_density() -> Self::Output {
todo!("complete the implementation; note that you might need to change the trait bounds and generics of the `impl` line");
}
}
// Now, any of our existing energy providers can be used with a mix fuel.
/// A function that returns the energy produced by the `OmniGenerator` with efficiency of 80%, when
/// the fuel type is an even a mix of `Diesel` as `LithiumBattery`;
pub fn omni_80_energy(amount: u32) -> BTU {
todo!();
}
// Finally, let's consider marker traits, and some trait bounds.
/// Some traits are just markers. They don't bring any additional functionality anything, other than
/// marking a type with some trait.
pub trait IsRenewable {}
impl IsRenewable for LithiumBattery {}
/// Define the following struct such that it only provides energy if the fuel is `IsRenewable`.
///
/// It has perfect efficiency.
pub struct GreenEngine<F: Fuel>(pub PhantomData<F>);
impl<F: Fuel> ProvideEnergy<F> for GreenEngine<F> {
fn provide_energy(&self, f: FuelContainer<F>) -> <F as Fuel>::Output {
todo!("complete the implementation; note that you might need to change the trait bounds and generics of the `impl` line");
}
}
/// Define the following struct such that it only provides energy if the fuel's output type is
/// `BTU`.
///
/// It has perfect efficiency.
pub struct BritishEngine<F: Fuel>(pub PhantomData<F>);
impl<F: Fuel> ProvideEnergy<F> for BritishEngine<F> {
fn provide_energy(&self, f: FuelContainer<F>) -> <F as Fuel>::Output {
todo!("complete the implementation; note that you might need to change the trait bounds and generics of the `impl` line");
}
}
// Congratulations! you have finished the advance trait section.
//
// Disclaimer: the types and traits that you are asked to implement in this module are by no means
// designed to be sensible. Instead, they are chosen to represent a typical, often difficult,
// pattern. Some are intentionally slightly convoluted to challenge you :). I am sure if we actually
// wanted to design a fuel system, we would do better.
/// This function is not graded. It is just for collecting feedback.
/// On a scale from 0 - 255, with zero being extremely easy and 255 being extremely hard,
/// how hard did you find this section of the exam.
pub fn how_hard_was_this_section() -> u8 {
todo!()
}
/// This function is not graded. It is just for collecting feedback.
/// How much time (in hours) did you spend on this section of the exam?
pub fn how_many_hours_did_you_spend_on_this_section() -> u8 {
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
trait ToBTU {
fn to_btu(self) -> BTU;
}
impl<T: Into<BTU>> ToBTU for T {
fn to_btu(self) -> BTU {
self.into()
}
}
#[test]
fn nuclear() {
let nr = NuclearReactor;
assert_eq!(
nr.provide_energy(FuelContainer::<Uranium>::new(10))
.to_btu(),
9900
);
assert_eq!(
nr.provide_energy(FuelContainer::<Uranium>::new(10))
.to_btu(),
9900
);
}
#[test]
fn ic_1() {
let ic = InternalCombustion::<3>::new(120);
assert_eq!(
ic.provide_energy(FuelContainer::<Diesel>::new(10)).to_btu(),
1000
);
assert_eq!(
ic.provide_energy(FuelContainer::<Diesel>::new(10)).to_btu(),
1000
);
assert_eq!(
ic.provide_energy(FuelContainer::<Diesel>::new(10)).to_btu(),
1000
);
assert_eq!(
ic.provide_energy(FuelContainer::<Diesel>::new(10)).to_btu(),
990
);
}
#[test]
fn omni_1() {
let og = OmniGenerator::<100>;
assert_eq!(
og.provide_energy(FuelContainer::<Uranium>::new(10))
.to_btu(),
10000
);
assert_eq!(
og.provide_energy(FuelContainer::<Diesel>::new(10)).to_btu(),
1000
);
assert_eq!(
og.provide_energy(FuelContainer::<LithiumBattery>::new(10))
.to_btu(),
2000
);
}
#[test]
fn mixed_1() {
assert_eq!(
Mixed::<Diesel, LithiumBattery>::energy_density().to_btu(),
150
);
}
#[test]
fn custom_mixed_1() {
// custom with 50 is the same as Mixed.
assert_eq!(
CustomMixed::<50, Diesel, LithiumBattery>::energy_density().to_btu(),
Mixed::<Diesel, LithiumBattery>::energy_density()
);
}
}
+106
View File
@@ -0,0 +1,106 @@
// Imagine you have an outcome enum like this.
#[derive(Clone)]
pub enum Outcome {
Ok,
SomethingWentWrong,
IDontKnow,
}
// A function takes some arbitrary input that's a collection of `T`, and processes each item
// individually. Each process can be an `Outcome`. We return `Vec<Outcome>`.
pub fn process_stuff<T>(input: impl Iterator<Item = T>) -> Vec<Outcome> {
unimplemented!("You are not expected to implement this function");
}
// What we want to achieve is a quick way (in terms of lines of code) to scan the output and
// determine how many were okay, how many were error, etc.
//
// A boring solution follows 🫣:
pub fn ok_count(outcomes: Vec<Outcome>) -> usize {
todo!();
}
pub fn something_went_wrong_count(outcomes: Vec<Outcome>) -> usize {
todo!();
}
pub fn i_dont_know_count(outcomes: Vec<Outcome>) -> usize {
todo!();
}
// This is quite lame. We want to be able to call these methods directly on the `Vec<Outcome>`. But
// how do we do this? We can't add a function to type `Vec`. This type is part of the standard
// library!
//
// Correct, but we can define a trait in the current module, and implement that for `Vec<_>`.
//
// This is a very common approach, and is called an "extension trait".
pub trait OutcomeCount {
fn ok_count(&self) -> usize;
fn something_went_wrong_count(&self) -> usize;
fn i_dont_know_count(&self) -> usize;
}
// First, implement this trait.
impl OutcomeCount for Vec<Outcome> {
fn ok_count(&self) -> usize {
todo!();
}
fn i_dont_know_count(&self) -> usize {
todo!();
}
fn something_went_wrong_count(&self) -> usize {
todo!();
}
}
// Now we can call these functions directly on `Vec<Outcome>`.
// But all of that is a lot of boilerplate. Wouldn't it be nice to have a `derive` macro that
// exactly does this, on any enum?
//
// So, for any `enum Foo { X, Y, .. }`, `#[derive(CountOf)]` would generate a trait `CountOfFoo`,
// with functions named `fn x_count`, `fn y_count` etc. Finally, it would implement `CountOfFoo` for
// `Vec<Foo>`.
//
// And heck, you could then easily implement it for other collections of `Foo`, such as
// `HashMap<_, Foo>` etc.
/// This function is not graded. It is just for collecting feedback.
/// On a scale from 0 - 255, with zero being extremely easy and 255 being extremely hard,
/// how hard did you find this section of the exam.
pub fn how_hard_was_this_section() -> u8 {
todo!()
}
/// This function is not graded. It is just for collecting feedback.
/// How much time (in hours) did you spend on this section of the exam?
pub fn how_many_hours_did_you_spend_on_this_section() -> u8 {
todo!()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn simple_functions() {
let x = vec![Outcome::Ok, Outcome::Ok, Outcome::IDontKnow];
assert_eq!(ok_count(x.clone()), 2);
assert_eq!(i_dont_know_count(x.clone()), 1);
assert_eq!(something_went_wrong_count(x), 0);
}
#[test]
fn extension_trait() {
let x = vec![Outcome::Ok, Outcome::Ok, Outcome::IDontKnow];
assert_eq!(x.ok_count(), 2);
assert_eq!(x.i_dont_know_count(), 1);
assert_eq!(x.something_went_wrong_count(), 0);
}
}
+107
View File
@@ -0,0 +1,107 @@
//! Here we test your ability to write macros.
//!
//! Make sure these macros are importable from the root of your crate, and usable in an external
//! crate.
// A common Rust macro is `vec![...]` used for creating vectors of literal values. Without this
// macro, one would need to create an empty vector, and push each item to it individually.
//
// Your task here is to create an analogous macro for creating hashmaps pre-populated with literal
// values. The macro should be called like follows:
//
// let map1: HashMap<u32, u32> = map![1 =>2, 3 => 4, 5 => 6)];
#[macro_export]
macro_rules! map {
( $($todo:tt)* ) => {
Default::default()
};
}
/// Next, write a macro that implements a `get` function on a type.
///
/// Observe the following `trait Get`. It is merely a way to convey a value of type `T` via a `type`
/// (ie) a struct that implements `Get`. An example of manually building one of such structs is what
/// you see in `struct Seven`. It works, but it is verbose. We want a macro that would automatically
/// generate this implementation for us, as such:
///
/// ```
/// use pba_entrance_exam::impl_get;
/// impl_get! {
/// // implements `Get<u32>` for `struct Six`
/// Six: u32 = 6;
/// // implements `Get<u16>` for `struct FortyTwo`
/// FortyTwo: u16 = 42;
/// }
/// ```
///
/// For now, your macro could only support literals as the right hand side of `=`. But you can think
/// about the ramifications of allowing arbitrary expressions as well.
///
/// An important detail that you need to be aware of is that this macro could be called from outside
/// of this file as well. This means that you need to reference `trait Get` with its full path,
/// using `$crate`. Read more: `https://doc.rust-lang.org/reference/macros-by-example.html#hygiene`
pub trait Get<T> {
fn get() -> T;
}
struct Seven;
impl Get<u32> for Seven {
fn get() -> u32 {
7
}
}
// note that you should first-thing change `$($todo:tt)*`, this simply means 'accept anything'.
#[macro_export]
macro_rules! impl_get {
( $($todo:tt)* ) => {};
}
/// This function is not graded. It is just for collecting feedback.
/// On a scale from 0 - 255, with zero being extremely easy and 255 being extremely hard,
/// how hard did you find this section of the exam.
pub fn how_hard_was_this_section() -> u8 {
todo!()
}
/// This function is not graded. It is just for collecting feedback.
/// How much time (in hours) did you spend on this section of the exam?
pub fn how_many_hours_did_you_spend_on_this_section() -> u8 {
todo!()
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
#[test]
fn map() {
let macro_generated: HashMap<u32, u32> = map![1 => 2, 3 => 4, 5 => 6];
let mut expected = HashMap::<u32, u32>::new();
expected.insert(1, 2);
expected.insert(3, 4);
expected.insert(5, 6);
assert_eq!(macro_generated, expected);
}
#[test]
fn impl_get() {
impl_get!(
// should generate `struct Foo` that implements `Get<u32>`
Foo: u32 = 10;
// should generate `pub struct Bar` that implements `Get<u32>`
pub Bar: u32 = 42;
// should generate `pub struct Baz` that has implements `Get<u16>`.
pub Baz: u16 = 21;
);
// you should be able to make these work.
// assert_eq!(Foo::get(), 10);
// assert_eq!(Bar::get(), 42);
// const CONST: u32 = Baz::get();
// assert_eq!(CONST, 42);
}
}
+16
View File
@@ -0,0 +1,16 @@
// NOTE: We allow dead code and unused variables and macros throughout the exam because they appear
// in many places in the starter code. You should remove this when you think you are done so that
// the CI can help you find potential mistakes.
#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_macros)]
pub mod a_honor_code;
pub mod b_multiple_choice;
pub mod d_pattern_matching;
pub mod e_common_traits;
pub mod f_iterators;
pub mod h_advanced_traits;
pub mod i_extension_traits;
pub mod k_macros;
pub mod m_builder;
+192
View File
@@ -0,0 +1,192 @@
//! In this module, we will explore the "builder" and "type-state" patterns in Rust, both of which
//! are extensively used in Substrate.
//!
//! There are ample resources about both of these online, so we will not go into too much detail
//! here. Here's one of the favorites of one of the instructors ;):
//! <https://www.youtube.com/watch?v=bnnacleqg6k>
//!
//! We will reuse the types from `e_common_traits` module and create a builder for the [`Employee`]
//! type.
use crate::e_common_traits::Employee;
/// First, let's build a naive builder. This builder should allow you to build an [`Employee`],
/// where the `name` and `uid` must be initialized, but the `experience` and `wage` can be left at
/// their default values, 0.
///
/// The final `fn build` return `Err(())` if either of `name` or `id` are not specified. See the
/// example section below.
///
/// > PS. Did you now know that the code snippets in your rust docs also compile, and are tested?
/// > now you do! :) `cargo test --doc` will run the tests.
///
/// ## Example
///
/// ```
/// # use pba_entrance_exam::m_builder::EmployeeBuilder;
///
/// # fn main() {
/// let success = EmployeeBuilder::default().name("John".to_string()).uid(42).build();
/// assert!(success.is_ok());
///
/// let fail = EmployeeBuilder::default().name("John".to_string()).build();
/// assert!(fail.is_err());
///
/// let fail = EmployeeBuilder::default().uid(42).build();
/// assert!(fail.is_err());
/// # }
/// ```
pub struct EmployeeBuilder {
name: Option<String>,
uid: Option<u32>,
experience: u32,
wage: u32,
}
impl Default for EmployeeBuilder {
fn default() -> Self {
Self {
name: None,
uid: None,
wage: 0,
experience: 0,
}
}
}
impl EmployeeBuilder {
pub fn name(self, name: String) -> Self {
todo!("finish the implementation.");
}
pub fn uid(self, uid: u32) -> Self {
todo!("finish the implementation.");
}
pub fn experience(self, experience: u32) -> Self {
todo!("finish the implementation.");
}
pub fn wage(self, wage: u32) -> Self {
todo!("finish the implementation.");
}
pub fn build(self) -> Result<Employee, ()> {
todo!("finish the implementation.");
}
}
// Okay, that was good, but the unfortunate thing about the previous approach is that we will have
// no way to notify the user about their potential failure to set the name or uid, until they call
// `build` at runtime. Isn't Rust all about using the type system to move runtime errors to compile
// time?
//
// > "Rust is a language that gives you compile-time errors instead of runtime errors. It's like
// > having a spell checker for your code." - Steve Klabnik
//
// With this mindset in mind, we will introduce a new pattern called "type-state" to help us achieve
// that.
/// A unique type explicitly representing an employee that has been named.
pub struct Named {
name: String,
}
/// A unique type explicitly representing an employee that NOT has been named.
pub struct NotNamed;
/// A unique type explicitly representing an employee that has been identified.
pub struct Identified {
uid: u32,
}
/// A unique type explicitly representing an employee that has NOT been identified.
pub struct UnIdentified;
/// A new builder that uses the "type-state" pattern to ensure that the user has set the name and
/// uid. The main trick here is that instead of having `name` be represented by `Option<String>`, we
/// have two unique types mimicking the `Option<_>`: `Named { .. }` is analogous to `Some(_)` and
/// `UnNamed` is analogous to `None`. But, `Option<_>` is jus ONE type, but `Named` and `UnNamed`
/// are two different types.
///
/// What's the benefit of that? we can make sure that the `fn build` is only implemented if both the
/// `Name` and `Id` generics are set to `Named` and `Identified`.
///
/// > Did you know that not only you can write tests in your rust-docs, as we did in
/// > [`EmployeeBuilder`], you can also write snippets of code that MUST FAIL TO COMPILE? Cool, eh?
/// > See: <https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html>
///
/// ## Example
///
/// ```
/// use pba_entrance_exam::m_builder::TypedEmployeeBuilder;
///
/// # fn main() {
/// // This is not a result anymore, because we guarantee at compile time that it has name and uid.
/// let employee =
/// TypedEmployeeBuilder::default().name("John".to_string()).uid(42).wage(77).build();
/// assert_eq!(employee.name, "John");
/// assert_eq!(employee.wage, 77);
/// assert_eq!(employee.uid, 42);
/// # }
/// ```
///
/// This code will simply fail to compile:
///
/// ```compile_fail
/// use pba_entrance_exam::m_builder::TypedEmployeeBuilder;
///
/// # fn main() {
/// let success = TypedEmployeeBuilder::default().uid(42).build();
/// # }
/// ```
pub struct TypedEmployeeBuilder<Name, Id> {
experience: u32,
wage: u32,
name: Name,
uid: Id,
}
impl Default for TypedEmployeeBuilder<NotNamed, UnIdentified> {
fn default() -> Self {
Self {
experience: 0,
wage: 0,
name: NotNamed,
uid: UnIdentified,
}
}
}
impl<Name, Id> TypedEmployeeBuilder<Name, Id> {
pub fn name(self, name: String) -> Self {
todo!("finish the implementation. Note that you might need to move some of these functions to a new `impl` blocks with different trait bounds, or change the return type to use `Named` etc.");
}
pub fn uid(self, uid: u32) -> Self {
todo!("finish the implementation. Note that you might need to move some of these functions to a new `impl` blocks with different trait bounds, or change the return type to use `Named` etc.");
}
pub fn experience(self, experience: u32) -> Self {
todo!("finish the implementation. Note that you might need to move some of these functions to a new `impl` blocks with different trait bounds, or change the return type to use `Named` etc.");
}
pub fn wage(self, wage: u32) -> Self {
todo!("finish the implementation. Note that you might need to move some of these functions to a new `impl` blocks with different trait bounds, or change the return type to use `Named` etc.");
}
pub fn build(self) -> Employee {
todo!("finish the implementation. Note that you might need to move some of these functions to a new `impl` blocks with different trait bounds, or change the return type to use `Named` etc.");
}
}
/// This function is not graded. It is just for collecting feedback.
/// On a scale from 0 - 255, with zero being extremely easy and 255 being extremely hard,
/// how hard did you find this section of the exam.
pub fn how_hard_was_this_section() -> u8 {
todo!()
}
/// This function is not graded. It is just for collecting feedback.
/// How much time (in hours) did you spend on this section of the exam?
pub fn how_many_hours_did_you_spend_on_this_section() -> u8 {
todo!()
}