Skip to content

Commit

Permalink
Moved parts from peter-kehl/no_std_rust_lib_presentation. Misc.
Browse files Browse the repository at this point in the history
  • Loading branch information
peter-lyons-kehl committed Oct 6, 2022
1 parent c13cbb3 commit 093c0bf
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 17 deletions.
131 changes: 128 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Scope

- Primary focus: Memory handling.

TODO
- beware that vscode.dev can't display inferred types (of `let` definitions), neither declared
parameter names in function calls
Expand All @@ -6,7 +10,7 @@ TODO
- VS-Code-like horizontal menu (if you have it hidden by default, toggle it with "Alt")
- your VS Code theme (if you use VS Code sync)
- `Ctrl + comma` shows VS-Code-like Settings
- Alternative online VS Code:
- Alternative: Online VS Code:
- https://vscode.dev/github/peter-kehl/no_std_rna_slice_patterns
- [Insiders (beta-like version)](https://insiders.vscode.dev/github/peter-kehl/no_std_rna_slice_patterns)
- these don't have a horizontal menu, but open it by the hamburger button
Expand All @@ -16,7 +20,7 @@ TODO

- `nightly` Rust compiler
- the actual solutions work with `stable` Rust. However, the test harness (with extras on top of of
Exercism's tests) needs `nightly` (as of mid 2022).
Exercism's tests) needs `nightly` Rust (as of mid 2022).

# List of patterns (and shared utils)
- [00_utils](https://github.com/peter-kehl/no_std_rna_slice_patterns/blob/main/00_utils/src/lib.rs)
Expand Down Expand Up @@ -80,4 +84,125 @@ implementation) doesn't introduce anything new related to `no_std`, but it fits
- All "unlimited" properties are constrained by available memory.
- (\*) indicates a property or implementation that isn't `no_std`-specific, or is specific to this workspace. It's here for clarification.

TODO Group implementations.
TODO Group implementations?

---

# Methods

# no_std with heap

Replace use of `HashSet/HashMap` with `BTreeSet/BTreeMap` when possible. Or create different data
structures. Or use 3rd party crates.

Replace `Arc` with `Rc` (since there is no multi-threading in `no_std`).

# no_std without heap

- Have your functions accept [slices](https://doc.rust-lang.org/nightly/book/ch04-03-slices.html)
(shared or mutable), rather than `Vec` or `String`, wherever possible. Both `Vec` and `String`
auto cast/expose themselves as a slice. (This is a good practice even with heap, or in `std`.)
- Similarly, whenever possible, have your `struct`-s and `enum`-s store references, or slices,
rather than own the data. It does involve
[`lifetimes`](https://www.cloudbees.com/blog/lifetimes-in-rust), but that can be a good practice,
too.

- Can't
[`core::iter::Iterator.collect()`](https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.collect).
- Even though `collect()` does exist in `core` (and not only in `std`), it can collect only to
implementations of
[`core::iter::FromIterator`](https://doc.rust-lang.org/core/iter/trait.FromIterator.html). (That,
again, exists in `core`, in addition to `std`). However, there are no `core`-only implementors of
`FromIterator` (other than collecting zero or one item [to
`core::option::Option`](https://doc.rust-lang.org/core/iter/trait.FromIterator.html#impl-FromIterator%3COption%3CA%3E%3E)
or [to
`core::result::Result`](https://doc.rust-lang.org/core/iter/trait.FromIterator.html#impl-FromIterator%3CResult%3CA%2C%20E%3E%3E)).
- `collect()` doesn't exist for arrays nor slices (without heap). Hence we need to iterate and
store in a (mutable) array or slice. _New to Rust? And Worried about side effects?_ Good news:
_Safe_ Rust prevents unintended side effects, because we "[cannot have a mutable reference while
we have an immutable
one](https://doc.rust-lang.org/nightly/book/ch04-02-references-and-borrowing.html#mutable-references)
to the same value."
- there is no dynamic/resizable data storage
- a `no_std` design needs to batch/buffer/limit the total data
- use slices (instead of arrays) as parameter types wherever possible
- design function as accepting (shared or mutable) slices
- functions may need to write to mutable slice parameters (instead of returning).

---

- _New to Rust?_ Mutating slices or references/arrays may sound less "functional". But, in Rust any
mutated parameters must be declared so. Any parameter that may be modified must be either
- [`borrowed`](https://doc.rust-lang.org/nightly/book/ch04-02-references-and-borrowing.html)
(passed) as a mutable reference or slice (which has exclusive access), or
- passing ownership of the object:
- [_"move"-d_](https://doc.rust-lang.org/nightly/book/ch04-01-what-is-ownership.html#ownership-and-functions)
(or [`clone()`-d](https://doc.rust-lang.org/core/clone/trait.Clone.html) first and then the
clone is moved), or
- [_copied_: Ownership > Stack-Only Data: Copy]
](<https://doc.rust-lang.org/nightly/book/ch04-01-what-is-ownership.html#stack-only-data-copy>)
if it implements [`Copy` ](https://doc.rust-lang.org/core/marker/trait.Copy.html) trait.
_New to Rust?_ A `trait` is similar to an `interface` in Java, a `virtual` abstract
field-less class with virtual methods in C++, or a `protocol` in some other languages.
- See also
[ownership](https://doc.rust-lang.org/nightly/book/ch04-00-understanding-ownership.html),
[borrowing](https://doc.rust-lang.org/nightly/book/ch04-02-references-and-borrowing.html) and
[lifetimes](https://doc.rust-lang.org/nightly/book/ch10-03-lifetime-syntax.html).
- alternatively, use [`const` generics](https://rust-lang.github.io/rfcs/2000-const-generics.html),
a subset of Rust [generics](https://doc.rust-lang.org/nightly/book/ch10-00-generics.html), for
both function parameters and return values
- make the array size (which has to be known in compile time) a `const` generic parameter
- beware that generics make the executable larger, and the build process takes longer; it helps to
combine (`const`) generics for some functions, and slices for other
- application's top level function(s) define the array(s), or array-containing structs/enums, on
stack. Then they call the processing functions with slices, or with const generic-sized arrays
(or their references)

---

- this way you can re-use the same processing functions
- if you can process the incoming data last in, first out (in LIFO/stack order), you could recurse
(possibly batching the data in an array at every recursion level)
- Have functions return an iterator wherever possible. (And use it for parameters, too. Again, a
good practice even in `std`.)
- may need to implement
[core::iter::Iterator](https://doc.rust-lang.org/core/iter/trait.Iterator.html) to represents
results of your transformation. Such iterators refer to the underlying iterator (data
source/origin) and they may keep some state on top of it (a state machine).
- You may want to combine/chain functions accepting and returning iterators. Use keyword
[`impl`](https://doc.rust-lang.org/nightly/book/ch10-02-traits.html#returning-types-that-implement-traits)
to define types like `impl Iterator<Item = YourItemType>`.

---

# no_std without heap > Alternatives to collect()

When developing for `no_heap`, we can't `collect()` from iterators. That makes some tasks that need
random access (access to all items at the same time, like sorting) difficult.

This would need limit on number of items to be known at build time. Then the caller would pass a
(mutable) reference to an array, or a slice, where we would manually collect the items (in a `for`
loop, or `.foreach()` closure).

---

# no_std without heap > Compound iterators

For some purposes we may not need access to all items if we don't need to access the whole
collection. Then all we do needs sequential access only, for example a one-to-one transformation,
and/or filtering.

For that we may need to implement a compound iterator. It would contain an underlying iterator (or
several iterators) over the source data. It iterates and transforms and/or filters the source data.

[Exercism Rust track](https://exercism.org/tracks/rust/exercises) > [RNA
Transcription](https://exercism.org/tracks/rust/exercises/rna-transcription)

In addition to the text of the assignment, it helps to see the
[tests](https://github.com/peter-kehl/x-rust/blob/main/rust/rna-transcription-std/tests/rna-transcription.rs)
(or the same tests used for [`no_std`
version](https://github.com/peter-kehl/x-rust/blob/main/rust/rna-transcription-no_std-no_heap/tests/rna-transcription.rs))

<https://github.com/peter-kehl/x-rust/blob/main/rust/rna-transcription-std/src/lib.rs>

<https://github.com/peter-kehl/x-rust/blob/main/rust/rna-transcription-no_std-no_heap/src/lib.rs>
33 changes: 19 additions & 14 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
https://github.com/hakimel/reveal.js/blob/master/index.html. It contains
tabs instead of spaces, because that's how the original file was. Keeping
them as tabs makes it easier to compare/update.
However, this uses a customized theme from https://github.com/peter-kehl/reveal.js.
-->
<html>
<head>
Expand All @@ -12,9 +13,10 @@
<link rel="icon" type="image/png" href="favicon.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">

<title>no_std (embedded/low level-compatible) patterns in Rust</title>

<!-- Clone https://github.com/hakimel/reveal.js next to this repo. -->
<!-- Clone https://github.com/peter-kehl/reveal.js next to this repo. -->
<!-- Unsure if we need reset.css, but https://revealjs.com demo uses it.
-->
<link rel="stylesheet" href="../reveal.js/dist/reset.css">
Expand All @@ -40,22 +42,32 @@
<link rel="stylesheet"
href="https://highlightjs.org/static/demo/styles/base16/windows-high-contrast.css"
id="highlight-theme">

<style type="text/css">
.reveal p {
text-align: left;
}
.reveal ul {
display: block;
}
.reveal ol {
display: block;
}
</style>
</head>
<body>
<div class="reveal">
<div class="slides">
<!-- TODO put all <section> inside one "slides" -->
<section data-markdown="../no_std_rna_slice_patterns/README.md"></section>
<section data-markdown="https://github.com/exercism/rust/blob/main/exercises/practice/rna-transcription/.docs/instructions.md"></section>
<section data-markdown="README_FIRST.md"></section>
<section data-markdown="README.md"></section>
<!-- Viewing this locally? Clone
https://github.com/peter-kehl/no_std_rna_slice_patterns
next to this repo -->
<!-- Viewing this other than from github.com/peter-kehl? Clone
https://github.com/peter-kehl/no_std_rna_slice_patterns next to this repo -->
</div>
</div>

<!-- Clone https://github.com/hakimel/reveal.js next to this repo. -->
<!-- Clone https://github.com/peter-kehl/reveal.js next to this repo. -->
<script src="../reveal.js/dist/reveal.js"></script>

<!-- The following JS order may not matter, but let's keep it the same
Expand All @@ -67,15 +79,12 @@
repo. -->
<script src="../revealjs-embed-code/embed-code.js"></script>

<!-- Clone https://github.com/hakimel/reveal.js next to this repo. -->
<script src="../reveal.js/plugin/highlight/highlight.js"></script>
<script src="../reveal.js/plugin/notes/notes.js"></script>

<!-- Clone https://denehyg.github.io/reveal.js-menu next to this repo.
-->
<script src="../reveal.js-menu/menu.js"></script>

<!-- Clone https://github.com/hakimel/reveal.js next to this repo. -->
<script src="../reveal.js/plugin/search/search.js"></script>
<script>
// More info about initialization & config:
Expand All @@ -94,10 +103,7 @@
RevealMarkdown,
EmbedCode,
RevealHighlight,
RevealNotes,

RevealMenu,

RevealSearch ],
// As per
// https://github.com/denehyg/reveal.js-menu#configuration
Expand Down Expand Up @@ -151,8 +157,7 @@
width: "100%",
margin: 0,
// Don't use: height: "100%". Otherwise <pre><code> is narrow.
//height: "100%", Don't use: disableLayout: true in general.
//disableLayout: true
// Don't use: disableLayout: true in general.
});
</script>
</body>
Expand Down

0 comments on commit 093c0bf

Please sign in to comment.