mirror of
https://github.com/pezkuwichain/kurdistan_blockchain-akademy.git
synced 2026-04-21 23:47:58 +00:00
🎉 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:
@@ -0,0 +1,2 @@
|
||||
**/target
|
||||
.vscode
|
||||
Generated
+7
@@ -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
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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! 🚀
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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")))
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<_>>());
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
@@ -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!()
|
||||
}
|
||||
Reference in New Issue
Block a user