COMP6991 Supplementary Exam
Getting Started
Create a new directory for this lab called exam_supp,
change to this directory,
and fetch the provided code for the exam
by running these commands:
mkdir exam_supp cd exam_supp 6991 fetch exam
Or, if you're not working on CSE, you can download the provided code as a tar file.
Exercise:
Exam Preamble
Starting time: 2026-01-07 14:00:00
Finishing time: 2026-01-07 17:00:00
Time for the exam: 3 hours
This exam contains 7 questions, each of equal weight (10 marks each).
Total number of marks: 70
Total number of practical programming questions: 3 (30 marks)
Total number of theoretical programming questions: 4 (40 marks)
You should attempt all questions.
Exam Condition Summary
- This exam is "Open Book"
- Joint work is NOT permitted in this exam
- You are NOT permitted to communicate (email, phone, message, talk) with anyone during this exam, except for the COMP6991 staff via cs6991.exam@cse.unsw.edu.au
- The exam paper is confidential, sharing it during or after the exam is prohibited.
- You are NOT permitted to submit code that is not your own
- You may NOT ask for help from online sources.
- Even after you finish the exam, on the day of the exam, do NOT communicate your exam answers to anyone. Some students have extended time to complete the exam.
- Do NOT place your exam work in any location, including file sharing services such as Dropbox or GitHub, accessible to any other person.
- Your zpass should NOT be disclosed to any other person. If you have disclosed your zpass, you should change it immediately.
- The use of AI assistants is strictly prohibited in this exam. This includes services such as Github Copilot, OpenAI ChatGPT, agentic coding tools, etc.
- You are allowed to use any resources from the course during the exam.
- You are allowed to use small amounts of code (< 10 lines) of general-purpose code (not specific to the exam) obtained from a site such as Stack Overflow or other publicly available resources. You should attribute the source of this code clearly in an accompanying comment.
Exam submissions will be checked, both automatically and manually, for any occurrences of plagiarism.
By starting this exam, as a student of The University of New South Wales, you do solemnly and sincerely declare that you have not seen any part of this specific examination paper for the above course prior to attempting this exam, nor have any details of the exam's contents been communicated to you. In addition, you will not disclose to any University student any information contained in the abovementioned exam for a period of 24 hrs after the exam. Violation of this agreement is considered Academic Misconduct and penalties may apply.
For more information, read the UNSW Student Code, or contact the Course Account.
- This exam comes with starter files.
- You will be able to commence the exam and fetch the files once the exam commences.
- You may complete the exam questions using any platform you wish (VLab, VSCode, etc). You should ensure that the platform works correctly.
- You may submit your answers, using the give command provided below each question.
- You can use give to submit as many times as you wish. Only the last submission will be marked.
- Do NOT leave it to the deadline to submit your answers. Submit each question when you finish working on it.
- Please make sure that you submit all your answers at the conclusion of the exam - running the autotests does not automatically submit your code.
- Autotests are available for all practical questions to assist in your testing. You can use the command:
6991 autotest - Passing autotests does not guarantee any marks. Remember to do your own testing!
- No marks are awarded for commenting - but you can leave comments for the marker to make your code more legible as needed
Language Restriction
- All practical programming questions must be answered entirely in Rust; you may not submit code in any other programming languages.
- You are not permitted to use third-party crates other than the standard library (std).
Fit to Sit
By sitting or submitting an assessment on the scheduled assessment date, a student is declaring that they are fit to do so and cannot later apply for Special Consideration.
If, during an exam a student feels unwell to the point that they cannot continue with the exam, they should take the following steps:
- Stop working on the exam and take note of the time
- Contact us immediately, using cs6991.exam@cse.unsw.edu.au, and advise us that you are unwell
- Immediately submit a Special Consideration application saying that you felt ill during the exam and were unable to continue
- If you were able to advise us of the illness during the assessment (as above), attach screenshots of this conversation to the Special Consideration application
Technical Issues
If you experience a technical issue, you should take the following steps:
- If your issue is with the connection to CSE, please follow the following steps:
- If you are using VLab: Try exiting VLAB and reconnecting again - this may put you on a different server, which may improve your connection. If you are still experiencing problems, you can try changing how you connect to the CSE servers. Consider:
- By using VSCode (with SSH-FS extension): https://www.cse.unsw.edu.au/~learn/homecomputing/vscode/
- By using SSH: https://taggi.cse.unsw.edu.au/FAQ/Logging_In_With_SSH/
-
If you are using VSCode remote-ssh:
Try disconnecting VSCode, and then changing the URL from
vscode.unsw.edu.autovscode2.unsw.edu.au. - If you are using SSH: Try disconnecting SSH and reconnecting again.
- If things are still NOT working, take screenshots of as many of the following as possible:
- error messages
- screen not loading
- timestamped speed tests
- power outage maps
- Contact should be made immediately to advise us of the issue at cs6991.exam@cse.unsw.edu.au
- A Special Consideration application should be submitted immediately after the conclusion of the assessment, along with the appropriate screenshots.
Exercise:
Q1: Theory (10 marks)
Q1.1 (2 marks)
This question will ask you to comment on the following Rust snippet, which does not compile.
fn main() { let mut x = 5; let y = &x; x += 1; println!("{y}"); }
Question:
- Identify and briefly explain which feature of the Rust language is preventing this code from compiling. (1 mark)
- Suggest one way to fix this code so it compiles successfully. (1 mark)
Write your answer in exam_q1/q1_1.txt.
When you are finished working on your answer, submit your work with give:
give cs6991 exam_q1_1 q1_1.txt
Q1.2 (4 marks)
This question will ask you to comment on the following Rust snippet, which compiles correctly.
fn main() { let value = "42"; // let 1 let value = value.parse::<i32>().unwrap(); // let 2 let value = value * 2; // let 3 println!("{value}"); }
Question:
- Identify the type of
valueafterlet 1. (1 mark) - Identify the type of
valueafterlet 2. (1 mark) - Identify the Rust feature that allows the same variable name to be reused across multiple
letstatements. (1 mark) - Briefly argue one advantage or one disadvantage of this feature. (1 mark)
Write your answer in exam_q1/q1_2.txt.
When you are finished working on your answer, submit your work with give:
give cs6991 exam_q1_2 q1_2.txt
Q1.3 (4 marks)
Here is a function that retrieves an element from an array, written in both C and Rust.
// C version int get_element(int index) { int numbers[3] = {1, 2, 3}; return numbers[index]; }
// Rust version fn get_element(index: usize) -> i32 { let numbers = [1, 2, 3]; numbers[index] }
Question:
- Explain what happens if
get_element(10)is called in the C version. (1 mark) - Explain what happens if
get_element(10)is called in the Rust version. (1 mark) - Explain the philosophy behind Rust's behaviour compared to C in this situation. (1 mark)
- Identify one potential disadvantage of Rust's approach. (1 mark)
Write your answer in exam_q1/q1_3.txt.
When you are finished working on your answer, submit your work with give:
give cs6991 exam_q1_3 q1_3.txt
Exercise:
Q2: Practical (10 marks)
You are building a search system for a digital library. The system needs to find text within books and return search results containing both the search term and the surrounding context.
We have written three items which are implemented correctly, but are missing lifetime annotations. Your task is to add the correct lifetime annotations.
use require_lifetimes::require_lifetimes; /// Returns the line from `book` that contains `query`, if any. /// (3 marks) #[require_lifetimes] pub fn get_context(book: &str, query: &str) -> Option<&str> { for line in book.lines() { if line.contains(query) { return Some(line); } } None } /// A search result containing the search term and its surrounding context. /// (2 marks) pub struct SearchResult { pub query: &str, pub context: &str, } /// Searches for `query` across all books in the library. /// Returns a SearchResult for each line containing the query. /// (5 marks) #[require_lifetimes] pub fn search_library(library: &Vec<&str>, query: &str) -> Vec<SearchResult> { let mut results = vec![]; for book in library { for line in book.lines() { if line.contains(query) { results.push(SearchResult { query, context: line, }); } } } results }
You must add lifetime annotations to:
get_context: a function that finds a line containing the query (3 marks)SearchResult: a struct holding the search term and its context (2 marks)search_library: a function that searches multiple books (5 marks)
You are only permitted to add, remove, or modify lifetime annotations.
You must not change any other code in src/lib.rs.
This is an example of the expected behaviour:
6991 cargo run --bin exam_q2
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/exam_q2`
Found context: Rust is great for systems programming
Search results:
query: 'the', context: 'Once upon the time'
query: 'the', context: 'In the beginning'
6991 cargo run --bin exam_q2_alt
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/exam_q2_alt`
Context: Goodbye world
Found 3 lazy lines:
'lazy' in: Jumps over the lazy dog
'lazy' in: A lazy afternoon
'lazy' in: With lazy clouds
Write your answer in exam_q2/src/lib.rs.
When you think your program is working,
you can use autotest
to run some simple automated tests:
6991 autotest
When you are finished working on your answer, submit your work with give:
cd src/ give cs6991 exam_q2 lib.rs
Exercise:
Q3: Theory (10 marks)
Q3.1 (4 marks)
Consider the following (simplified) definition of the Iterator trait
from the standard library:
trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item>; fn count(self) -> usize where Self: Sized { let mut count = 0; while self.next().is_some() { count += 1; } count } }
Question:
- Identify what must be provided in order to implement
Iteratorfor a custom type. (2 marks) - Explain how it is possible for
countto have an implementation inside the trait definition. (1 mark) - Identify any trait bounds required by
fn count. (1 mark)
Write your answer in exam_q3/q3_1.txt.
When you are finished working on your answer, submit your work with give:
give cs6991 exam_q3_1 q3_1.txt
Q3.2 (3 marks)
The following function does not compile:
fn process<T>(items: Vec<T>) -> Option<T> where T: Clone { Some(items.iter().max()?.clone()) }
Question:
- Identify the missing trait bound causing the compilation error. (2 marks)
- The function body can be rewritten to remove the
Clonebound. Modify the function body such that the bound is no longer required. (1 mark)
Write your answer in exam_q3/q3_2.txt.
When you are finished working on your answer, submit your work with give:
give cs6991 exam_q3_2 q3_2.txt
Q3.3 (3 marks)
Consider the following code that creates a collection of plugins:
trait Plugin { fn name(&self) -> &str; fn execute(&self); } struct Logger; struct Validator; impl Plugin for Logger { fn name(&self) -> &str { "Logger" } fn execute(&self) { println!("Logging..."); } } impl Plugin for Validator { fn name(&self) -> &str { "Validator" } fn execute(&self) { println!("Validating..."); } } fn main() { let plugins: Vec<Box<dyn Plugin>> = vec![ Box::new(Logger), Box::new(Validator), ]; for plugin in &plugins { println!("{}: ", plugin.name()); plugin.execute(); } }
Question:
- Explain why
Boxis required here rather thanVec<dyn Plugin>directly. (1 mark) - Describe the limitation you would encounter if you used
Vec<T> where T: Plugininstead ofVec<Box<dyn Plugin>>. Furthermore, explain why this limitation exists. (2 marks)
Write your answer in exam_q3/q3_3.txt.
When you are finished working on your answer, submit your work with give:
give cs6991 exam_q3_3 q3_3.txt
Exercise:
Q4: Theory (10 marks)
Q4.1 (4 marks)
Consider the following code that increments a shared counter across multiple threads:
use std::sync::{Arc, Mutex}; use std::thread; fn main() { let counter = Arc::new(Mutex::new(0)); let handles: Vec<_> = (0..10).map(|_| { let counter = Arc::clone(&counter); thread::spawn(move || { *counter.lock().unwrap() += 1; }) }).collect(); for handle in handles { handle.join().unwrap(); } println!("Final: {}", *counter.lock().unwrap()); }
Question:
- The
Mutexoutlives all spawned threads due to thejoin()calls. Explain whyArcis still required in this code. (1 mark) - Knowing the
Mutexoutlives the spawned threads, identify an alternative approach that would avoid the need forArc, and explain why that approach works withoutArc. (2 marks) - Explain what would happen, and why, if
Rcwas used instead ofArc. (1 mark)
Write your answer in exam_q4/q4_1.txt.
When you are finished working on your answer, submit your work with give:
give cs6991 exam_q4_1 q4_1.txt
Q4.2 (3 marks)
The following code attempts to spawn a thread that uses some data from the main thread:
use std::thread; fn main() { let data = vec![1, 2, 3]; let handle = thread::spawn(|| { println!("{data:?}"); }); handle.join().unwrap(); }
However, this code does not compile.
Question:
- Explain why this code does not compile. (1 mark)
- Adding the
movekeyword before||fixes the compilation error. Explain whatmovedoes to fix the issue. (1 mark) - If the programmer needed to use
datain the main thread after spawning (usingthread::spawnwith amoveclosure), describe how they could modify the code to allow this. An answer containing only the correctly modified code is sufficient. (1 mark)
Write your answer in exam_q4/q4_2.txt.
When you are finished working on your answer, submit your work with give:
give cs6991 exam_q4_2 q4_2.txt
Q4.3 (3 marks)
You are tasked with implementing a repeat_vec! macro that creates a Vec
with elements repeated a specified number of times.
The macro should use the following syntax, where each element is preceded by the number of times to repeat it and "of":
let v = repeat_vec![3 of 1, 2 of 2]; // v is vec![1, 1, 1, 2, 2] let greetings = repeat_vec![2 of "hello", 1 of "world"]; // greetings is vec!["hello", "hello", "world"] let single = repeat_vec![5 of 0]; // single is vec![0, 0, 0, 0, 0]
Question:
Implement the repeat_vec! macro.
Provide the full macro_rules! declaration.
You will receive (1 mark) for correctly implementing the macro
with only single element support (i.e. repeat_vec![N of expr]),
or (3 marks) for correctly implementing the macro supporting multiple elements with repetition.
Partial marks may be awarded for progress made.
Write your answer in exam_q4/q4_3.txt.
When you are finished working on your answer, submit your work with give:
give cs6991 exam_q4_3 q4_3.txt
Exercise:
Q5: Practical (10 marks)
Alex is building a data processing library called Processor.
It wraps a Vec and provides convenient methods for
transforming, filtering, and sorting data.
Alex started by making the system work only with String items.
However, users want to process other types too!
Alex needs to make the struct and methods generic to accommodate this.
It's late, so Alex has asked you to help finish the implementation. All that's needed is to make it as generic as possible!
You have been given the following starter code in src/lib.rs:
use std::collections::HashSet; pub struct Processor { items: Vec<String>, } impl Processor { pub fn new(items: Vec<String>) -> Self { Self { items } } pub fn map_all(&self, f: fn(&String) -> String) -> Vec<String> { self.items.iter().map(f).collect() } pub fn filter_dedup(&self, pred: fn(&String) -> bool) -> Vec<String> { let mut seen = HashSet::new(); self.items .iter() .filter(|x| pred(x) && seen.insert((*x).clone())) .cloned() .collect() } pub fn into_sorted(self, f: fn(Vec<String>) -> Vec<String>) -> Vec<String> { let mut result = f(self.items); result.sort(); result } }
This code correctly implements Processor for String items.
Your task is to make this struct and its methods far more generic. You will need to make the following changes:
- Support items of any type, not just
String. - Support mapping to any output type, not just
String. - Convert the function pointers to appropriate closure types.
You will be required to constrain some generic type parameters as you solve the exercise. You must ensure that you do not overly constrain the types, only requiring what is minimally needed.
The code already runs correctly on a basic test case:
6991 cargo run --bin exam_q5
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/exam_q5`
Mapped: ["HELLO", "WORLD"]
Filtered unique: ["hello", "world"]
Sorted: ["goodbye", "hello", "world"]
However, the code does not yet typecheck on the other test cases provided.
Once you have modified the types to make the Processor as generic
as possible, you should find these other test cases now compile and run:
6991 cargo run --bin exam_q5_alt
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/exam_q5_alt`
Mapped: [1, 4, 9, 16, 25]
Filtered unique: [2, 4] (predicate called 7 times)
Sorted: [1, 2, 3, 4, 5, 100, 200]
6991 cargo run --bin exam_q5_alt2
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/exam_q5_alt2`
Mapped: [2, 4, 6]
Filtered unique: [Filterable(2), Filterable(3)]
Sorted: [Sortable(1), Sortable(2), Sortable(3)]
6991 cargo run --bin exam_q5_alt3
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/exam_q5_alt3`
Mapped: [2, 8, 24]
Filtered unique: [1, 2]
Dropped: FnOnce consumed
Sorted: [1, 2, 3, 4, 5]
Partial marks will be awarded for solutions which pass a subset of the test cases.
Write your answer in exam_q5/src/lib.rs.
When you think your program is working,
you can use autotest
to run some simple automated tests:
6991 autotest
When you are finished working on your answer, submit your work with give:
cd src/ give cs6991 exam_q5 lib.rs
Exercise:
Q6: Theory (10 marks)
Q6.1 (4 marks)
When writing unsafe Rust code,
programmers take on additional responsibilities
that are normally handled by the compiler.
Question:
- Explain what it means for
unsafecode to be "sound". (2 marks) - If a bug exists in an
unsafeblock, can that bug affect code outside of theunsafeblock? Discuss. (2 marks)
Write your answer in exam_q6/q6_1.txt.
When you are finished working on your answer, submit your work with give:
give cs6991 exam_q6_1 q6_1.txt
Q6.2 (6 marks)
The following code attempts to write an
implementation of a Cell<T>,
making use of unsafe code.
use std::cell::UnsafeCell; pub struct Cell<T> { value: UnsafeCell<T>, } impl<T> Cell<T> { pub fn new(value: T) -> Self { Self { value: UnsafeCell::new(value), } } pub fn set(&self, value: T) { unsafe { *self.value.get() = value; } } pub fn replace(&self, value: T) -> T { unsafe { std::mem::replace(&mut *self.value.get(), value) } } pub fn get(&self) -> T where T: Copy, { unsafe { *self.value.get() } } pub fn get_ref(&self) -> &T { unsafe { &*self.value.get() } } pub fn get_mut(&mut self) -> &mut T { self.value.get_mut() } }
Cell provides interior mutability for Copy types,
allowing mutation through a shared reference.
It is commonly used for simple values like counters or flags
that need to be modified even when only a shared reference is available.
The following example code aims to demonstrate how this type can be used:
use unsafe_review::Cell; fn main() { let mut cell = Cell::new(10); println!("Initial: {}", cell.get()); cell.set(20); println!("After set: {}", cell.get()); let old = cell.replace(30); println!("After replace: {}, got back: {old}", cell.get()); let r = cell.get_ref(); println!("Via get_ref: {r}"); let m = cell.get_mut(); *m = 99; println!("Via get_mut: {}", cell.get()); }
When executed, the program outputs:
Initial: 10 After set: 20 After replace: 30, got back: 20 Via get_ref: 30 Via get_mut: 99
The implementation appears to work correctly for this simple example.
Question:
- Perform a brief code review on the
Cell<T>implementation, with respect to the idiomatic usage ofunsafeRust. Soundness should not be considered, in this part. (1 mark) - There exists a subtle unsoundness in
Cell<T>, allowing a user to provoke a segmentation fault in safe Rust. Explain the soundness issue. (3 marks) - Write an example
fn mainthat exploits the unsoundness inCell<T>to provoke a segmentation fault in safe Rust. (2 marks)
An answer causing memory corruption other than a segmentation fault will be awarded only one mark.
Write your answer in exam_q6/q6_2.txt.
When you are finished working on your answer, submit your work with give:
give cs6991 exam_q6_2 q6_2.txt
Exercise:
Q7: Practical (10 marks)
StackLang Interpreter
In this question, you will implement an interpreter for StackLang, a simple stack-based programming language.
Stack-based languages operate on a stack of values. Operations pop values from the stack, perform computation, and push results back. This is also known as Reverse Polish Notation (RPN) or postfix notation.
For example, to compute 3 + 4 in StackLang, you write 3 4 +:
| Step | Token | Action | Stack |
|---|---|---|---|
| 1 | 3 |
Push 3 | [3] |
| 2 | 4 |
Push 4 | [3, 4] |
| 3 | + |
Pop 4 and 3, push 7 | [7] |
Input Format
Your program reads a single line from standard input containing a StackLang program. Tokens are separated by whitespace.
Output Format
After executing the program, print each value remaining on the stack,
one per line, from bottom to top.
If the stack is empty, print nothing.
Boolean values should be printed as true and false.
Error Handling
All test inputs are guaranteed to be valid StackLang programs. You do not need to handle errors such as stack underflow, division by zero, undefined variables, or type mismatches.
Stage 1: Numbers and Arithmetic (2 marks)
Implement the following features:
- Integer literals: Push the integer onto the stack. May be negative (e.g.,
-5). +: Pop two values, push their sum.-: Pop two values, push their difference. (10 3 -→7)*: Pop two values, push their product./: Pop two values, push their quotient, truncated towards zero. (10 3 /→3)%: Pop two values, push their remainder. (10 3 %→1)
Examples
echo "3 4 +" | 6991 cargo run 7 echo "10 3 -" | 6991 cargo run 7 echo "6 7 *" | 6991 cargo run 42 echo "17 5 /" | 6991 cargo run 3 echo "17 5 %" | 6991 cargo run 2 echo "1 2 3" | 6991 cargo run 1 2 3 echo "2 3 + 4 *" | 6991 cargo run 20
Stage 2: Stack Manipulation (1 mark)
Implement the following stack operations:
dup: Duplicate the top value.[a]→[a, a]drop: Discard the top value.[a]→[]swap: Swap the top two values.[a, b]→[b, a]over: Copy the second value to the top.[a, b]→[a, b, a]rot: Rotate the top three values.[a, b, c]→[b, c, a]
Examples
echo "5 dup" | 6991 cargo run 5 5 echo "1 2 3 drop" | 6991 cargo run 1 2 echo "1 2 swap" | 6991 cargo run 2 1 echo "1 2 over" | 6991 cargo run 1 2 1 echo "1 2 3 rot" | 6991 cargo run 2 3 1 echo "3 dup *" | 6991 cargo run 9
Stage 3: Comparisons and Booleans (2 marks)
Implement boolean values and comparison operations:
true: Push the boolean valuetrue.false: Push the boolean valuefalse.<: Pop two integers, pushtrueif less-than. (3 5 <→true)>: Pop two integers, pushtrueif greater-than. (3 5 >→false)=: Pop two integers, pushtrueif equal. (5 5 =→true)and: Pop two booleans, push their logical AND.or: Pop two booleans, push their logical OR.not: Pop one boolean, push its logical negation.
Examples
echo "3 4 <" | 6991 cargo run true echo "5 5 =" | 6991 cargo run true echo "10 3 >" | 6991 cargo run true echo "true false and" | 6991 cargo run false echo "true false or" | 6991 cargo run true echo "true not" | 6991 cargo run false echo "3 5 < 10 10 = and" | 6991 cargo run true
Stage 4: Variables (2 marks)
Implement variable storage and retrieval:
set <name>: Pop the top value and store it in variablename.get <name>: Push the value of variablenameonto the stack.
Variable names consist of letters and underscores only (e.g., x, counter, my_var).
All variables have global scope.
Examples
echo "10 set x get x get x +" | 6991 cargo run 20 echo "5 set a 3 set b get a get b *" | 6991 cargo run 15 echo "1 set x get x 1 + set x get x" | 6991 cargo run 2 echo "true set flag get flag not" | 6991 cargo run false
Stage 5: Conditionals (1 mark)
Implement conditional execution:
if <then-body> then: Pop a boolean. Iftrue, executethen-body.if <then-body> else <else-body> then: Pop a boolean. Iftrue, executethen-body; otherwise executeelse-body.
Conditionals may be nested.
Examples
echo "5 3 > if 1 then" | 6991 cargo run 1 echo "5 3 < if 1 then" | 6991 cargo run echo "5 3 > if 100 else 200 then" | 6991 cargo run 100 echo "5 3 < if 100 else 200 then" | 6991 cargo run 200 echo "true if true if 1 then then" | 6991 cargo run 1 echo "10 set x get x 5 > if get x 2 * else get x 2 / then" | 6991 cargo run 20
Stage 6: Loops and Functions (2 marks)
Implement loops and user-defined functions:
Loops
loop <body> end: Executebodyrepeatedly untilbreakis encountered.break: Exit the innermost loop immediately.
Functions
fn <name> <body> end: Define a function with the given name and body. The function is not executed at definition time.call <name>: Execute the function with the given name.
Functions may call other functions, including themselves (recursion).
Loops may be nested, and break only exits the innermost loop.
Examples
echo "0 loop dup 5 < if dup 1 + else break then end" | 6991 cargo run 0 1 2 3 4 5 echo "fn square dup * end 5 call square" | 6991 cargo run 25 echo "fn double 2 * end 3 call double call double" | 6991 cargo run 12 echo "fn fact dup 1 > if dup 1 - call fact * then end 5 call fact" | 6991 cargo run 120 echo "1 set i loop get i 5 > if break then get i get i 1 + set i end drop" | 6991 cargo run 1 2 3 4
Summary of Marks
| Stage | Features | Marks |
|---|---|---|
| 1 | Numbers and arithmetic (+, -, *, /, %) |
2 |
| 2 | Stack manipulation (dup, drop, swap, over, rot) |
1 |
| 3 | Comparisons and booleans (<, >, =, and, or, not) |
2 |
| 4 | Variables (set, get) |
2 |
| 5 | Conditionals (if...then, if...else...then) |
1 |
| 6 | Loops and functions (loop...end, break, fn...end, call) |
2 |
| Total | 10 | |
Partial marks may be awarded for partially working implementations within each stage.
Write your answer in exam_q7/src/main.rs.
When you think your program is working,
you can use autotest
to run some simple automated tests:
6991 autotest
When you are finished working on your answer, submit your work with give:
cd src/ give cs6991 exam_q7 main.rs
Submission
When you are finished each exercise make sure you submit your work by running give.
You can run give multiple times. Only your last submission will be marked.
Don't submit any exercises you haven't attempted.
Do not leave it to the deadline to submit your answers. Submit each question when you finish working on it. Running autotests does not automatically submit your code.
If you are working at home, you may find it more convenient to upload your work via give's web interface.
Remember you have until 2026-01-07 17:00:00, Sydney time, to complete this exam (not including any extra time provided by ELPs).
You cannot obtain marks by e-mailing your code to tutors or lecturers.
6991 classrun -check exam_q1_1 6991 classrun -check exam_q1_2 6991 classrun -check exam_q1_3 6991 classrun -check exam_q2 ... 6991 classrun -check exam_q7
- End of examination. -