cookbook home ~ main site ~ links ~ podcast ~ mastodon

Step-By-Step Code Example Prototype 4

Details

fn main() { 
fn main() { 
 
  let alfa = String::from("apple"); 
  let alfa = String::from("apple"); 
 
  show_value(&alfa); 
  show_value(&alfa); 
 
  println!("alfa is {alfa}"); 
  println!("alfa is {alfa}"); 
 
} 
} 
 
 
 
 
fn show_value(value: &String) {  
fn show_value(value: &String) {  
 
  println!("show_value got {value}"); 
  println!("show_value got {value}"); 
 
} 
} 
 
  • Start with an empty main function.

HTML

<div id="blockGrid">
  <div id="codeBlock"></div>
  <div id="contentArea"></div>
  <div id="buttonWrapper"></div>
</div>

CSS

/* PrismJS 1.29.0
https://prismjs.com/download.html#themes=prism-okaidia&languages=markup+css+clike+javascript+applescript+bash+css-extras+go+jq+json+lisp+lua+makefile+markdown+nginx+perl+python+jsx+tsx+regex+ruby+rust+shell-session+sql+typescript+vim+wasm+yaml&plugins=line-highlight+line-numbers+file-highlight+highlight-keywords+remove-initial-line-feed */
code[class*='language-'],
pre[class*='language-'] {
  color: #f8f8f2;
  /*
  background: 0 0;
  */
  text-shadow: 0 1px rgba(0, 0, 0, 0.3);
  text-align: left;
  white-space: pre;
  word-spacing: normal;
  word-break: normal;
  word-wrap: normal;
  /*
  line-height: 1.5;
  */
  -moz-tab-size: 4;
  -o-tab-size: 4;
  tab-size: 4;
  -webkit-hyphens: none;
  -moz-hyphens: none;
  -ms-hyphens: none;
  hyphens: none;
}

pre[class*='language-'] {
  /*
  padding: 1em;
  margin: 0.5em 0;
  */
  overflow: auto;
  /*
  border-radius: 0.3em;
  */
}
:not(pre) > code[class*='language-'],
pre[class*='language-'] {
  /*
  background: #272822;
  */
}
:not(pre) > code[class*='language-'] {
  /*
  padding: 0.1em;
  border-radius: 0.3em;
  */
  white-space: normal;
}
.token.cdata,
.token.comment,
.token.doctype,
.token.prolog {
  color: #8292a2;
}
.token.punctuation {
  color: #f8f8f2;
}
.token.namespace {
  opacity: 0.7;
}
.token.constant,
.token.deleted,
.token.property,
.token.symbol,
.token.tag {
  color: #f92672;
}
.token.boolean,
.token.number {
  color: #ae81ff;
}
.token.attr-name,
.token.builtin,
.token.char,
.token.inserted,
.token.selector,
.token.string {
  color: #a6e22e;
}
.language-css .token.string,
.style .token.string,
.token.entity,
.token.operator,
.token.url,
.token.variable {
  color: #f8f8f2;
}
.token.atrule,
.token.attr-value,
.token.class-name,
.token.function {
  color: #e6db74;
}
.token.keyword {
  color: #66d9ef;
}
.token.important,
.token.regex {
  color: #fd971f;
}
.token.bold,
.token.important {
  font-weight: 700;
}
.token.italic {
  font-style: italic;
}
.token.entity {
  cursor: help;
}
pre[data-line] {
  position: relative;
  /*
  padding: 1em 0 1em 3em;
  */
}
.line-highlight {
  position: absolute;
  left: 0;
  right: 0;
  /*
  padding: inherit 0;
  margin-top: 1em;

  background: hsla(24, 20%, 50%, 0.08);
  background: linear-gradient(
    to right,
    hsla(24, 20%, 50%, 0.1) 70%,
    hsla(24, 20%, 50%, 0)
  );
  */
  pointer-events: none;
  /*
  line-height: inherit;
  */
  white-space: pre;
}
@media print {
  .line-highlight {
    -webkit-print-color-adjust: exact;
    color-adjust: exact;
  }
}
.line-highlight:before,
.line-highlight[data-end]:after {
  content: attr(data-start);
  position: absolute;
  top: 0.4em;
  left: 0.6em;
  min-width: 1em;
  /*
  padding: 0 0.5em;
  background-color: hsla(24, 20%, 50%, 0.4);
  */
  color: #f4f1ef;
  text-align: center;
  vertical-align: 0.3em;
  border-radius: 999px;
  text-shadow: none;
  box-shadow: 0 1px #fff;
}
.line-highlight[data-end]:after {
  content: attr(data-end);
  top: auto;
  /*
  bottom: 0.4em;
  */
}
.line-numbers .line-highlight:after,
.line-numbers .line-highlight:before {
  content: none;
}
pre[id].linkable-line-numbers span.line-numbers-rows {
  pointer-events: all;
}
pre[id].linkable-line-numbers span.line-numbers-rows > span:before {
  cursor: pointer;
}
pre[id].linkable-line-numbers span.line-numbers-rows > span:hover:before {
  /*
  background-color: rgba(128, 128, 128, 0.2);
  */
}
pre[class*='language-'].line-numbers {
  position: relative;
  /*
  padding-left: 3.8em;
  */
  counter-reset: linenumber;
}
pre[class*='language-'].line-numbers > code {
  position: relative;
  white-space: inherit;
}
.line-numbers .line-numbers-rows {
  position: absolute;
  pointer-events: none;
  top: 0;
  /*
  font-size: 100%;
  */
  left: -3.8em;
  width: 3em;
  letter-spacing: -1px;
  border-right: 1px solid #999;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.line-numbers-rows > span {
  display: block;
  counter-increment: linenumber;
}
.line-numbers-rows > span:before {
  content: counter(linenumber);
  color: #999;
  display: block;
  /*
  padding-right: 0.8em;
  */
  text-align: right;
}

#contentArea {
  min-height: 20rem;
  background-color: #667;
  padding: 0.3rem;
  border-radius: 0.4rem;
  margin-left: 0.6rem;
}

#codeCol {
  margin: 0.6rem;
}

#codeBlock {
  min-height: 20rem;
  background-color: #000;
  border-radius: 0.4rem;
  padding: 0.7rem;
  margin-right: 0.4rem;
}

.hideit {
  display: none;
}

pre {
  color: #667;
}

.activeButton {
  background-color: #080;
  font-weight: bold;
}

#blockGrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

#buttonWrapper {
  margin-top: 0.8rem;
}

#nextSet {
  margin-left: 0.6rem;
}

#previousSet {
  margin-right: 0.6rem;
}

JavaScript

const rawSourceCode = `fn main() {
  let alfa = String::from("apple");
  show_value(&alfa);
  println!("alfa is {alfa}");
}

fn show_value(value: &String) { 
  println!("show_value got {value}");
}`.split('\n')

const loadSourceCode = () => {
  s.sourceCode = []
  rawSourceCode.forEach((rawLine) => {
    const lineSplit = rawLine.split(' // ')
    s.sourceCode.push([...lineSplit])
  })
}

const s = {
  currentLineSet: 1,
  lineMarkers: [],
}

const lineSets = [
  {
    lines: [],
  },

  {
    lines: [`0_r`, `0_s`, `0_s`, `0_s`, `0_r`, `0_s`, `0_s`, `0_s`, `0_s`],
    text: `<ul>
    <li>Start with an empty <code>main</code> function.</li>
    </ul>`,
  },

  {
    lines: [`0_c`, `0_s`, `0_s`, `0_s`, `0_c`, `0_s`, `0_r`, `0_s`, `0_r`],
    text: `
    <ul>
    <li>The first thing we'll do is to setup a function to demonstrate passing the reference</li>
    <li>Create a second <code>show_value</code> function that takes one parameter</li>
    <li>Make the parameter a <code>String</code> reference that gets bound to a function variable named <code>value</code></li>
    <li>We know a reference because it starts with an <code>&amp;</code></li>
    </ul>`,
  },

  {
    lines: [`0_c`, `0_s`, `0_s`, `0_s`, `0_c`, `0_s`, `0_c`, `0_r`, `0_c`],
    text: `<ul>
    <li>Print out the <code>value</code> reference variable inside <code>show_value</code> to confirm it's available</li>
    </ul>`,
  },

  {
    lines: [`0_c`, `0_r`, `0_s`, `0_s`, `0_c`, `0_s`, `0_c`, `0_c`, `0_c`],
    text: `<ul>
    <li>Back in the <code>main</code> function, create the <code>alfa</code> variable we'll use with a <code>String</code> value of <code>apple</code> bound to it</li>
    </ul>
    `,
  },

  {
    lines: [`0_c`, `0_c`, `0_r`, `0_s`, `0_c`, `0_s`, `0_c`, `0_c`, `0_c`],
    text: `<ul>
    <li>Setup the call to the <code>show_value</code> function</li>
    <li>We pass a reference to <code>main</code>'s <code>alfa</code> variable by using it as an argument with the <code>&amp;</code> sign in front of it</li>
    </ul>
    `,
  },

  {
    lines: [`0_c`, `0_c`, `0_c`, `0_r`, `0_c`, `0_s`, `0_c`, `0_c`, `0_c`],
    text: `<ul>
    <li>Finally, print out the <code>alfa</code> variable in main.</li>
    <li>Because we used a reference with <code>show_value</code> ownership of the <code>String</code> value stayed with <code>alfa</code>. That means it can still access it for printing.</li>
    <li>If we hadn't used a reference, ownership would have been transferred into <code>show_value</code> and this <code>println!()</code> would have caused an error</li>
    </ul>`,
  },

  {
    lines: [`0_c`, `0_c`, `0_c`, `0_c`, `0_c`, `0_s`, `0_c`, `0_c`, `0_c`],
    text: `<ul><li>Running the code produces the output</li></ul>
    <pre>show_value got apple
alfa is apple</pre>
    `,
  },
]

const log = (msg) => {
  console.log(msg)
}

const handleNextButtonClick = () => {
  if (s.currentLineSet < lineSets.length - 1) {
    s.currentLineSet += 1
  }
  updateLines()
  updateButtons()
}

const handlePreviousButtonClick = () => {
  if (s.currentLineSet > 1) {
    s.currentLineSet -= 1
  }
  updateLines()
  updateButtons()
}

const updateLines = () => {
  const codeLineMarkerEls = document.getElementsByClassName('codeLineMarker')
  for (let x = 0; x < codeLineMarkerEls.length; x++) {
    codeLineMarkerEls[x].classList.add('hideit')
    codeLineMarkerEls[x].classList.remove('highlightCode')
  }

  for (let lineIndex = 0; lineIndex < s.sourceCode.length; lineIndex++) {
    const code = lineSets[s.currentLineSet].lines[lineIndex]
    window[`codeLine_${lineIndex}_${code}`].classList.remove('hideit')
    if (s.currentLineSet > 0) {
      const code_parts = code.split('_')
      if (code_parts[1] === 'r') {
        window[`codeLine_${lineIndex}_${code}`].classList.add('highlightCode')
      }
    }
  }
  window.contentArea.innerHTML = lineSets[s.currentLineSet].text
}

const updateButtons = () => {
  for (let lineSetIndex = 1; lineSetIndex < lineSets.length; lineSetIndex++) {
    if (s.currentLineSet === lineSetIndex) {
      window[`stepButton_${lineSetIndex}`].classList.add('activeButton')
    } else {
      window[`stepButton_${lineSetIndex}`].classList.remove('activeButton')
    }
  }
}

const makePreLines = () => {
  for (let lineIndex = 0; lineIndex < s.sourceCode.length; lineIndex++) {
    const codeLineEl = document.createElement('div')
    codeLineEl.classList.add('codeLineWrapper')
    codeLineEl.id = `codeLineWrapper${lineIndex}`
    window.codeBlock.appendChild(codeLineEl)
  }
}

const makeBaseLines = () => {
  s.sourceCode.forEach((sourceLineBatch, batchIndex) => {
    sourceLineBatch.forEach((sourceLine, lineIndex) => {
      const newLineRust = document.createElement('pre')
      newLineRust.classList.add('language-rust')
      newLineRust.classList.add('hideit')
      newLineRust.classList.add('codeLineMarker')
      newLineRust.id = `codeLine_${batchIndex}_${lineIndex}_r`
      newLineRust.innerHTML = `<code>${sourceLine}</code> `
      window[`codeLineWrapper${batchIndex}`].appendChild(newLineRust)

      const newLineCustom = document.createElement('pre')
      newLineCustom.id = `codeLine_${batchIndex}_${lineIndex}_c`
      newLineCustom.innerHTML = `${sourceLine} `
      newLineCustom.classList.add('hideit')
      newLineCustom.classList.add('codeLineMarker')
      window[`codeLineWrapper${batchIndex}`].appendChild(newLineCustom)

      const newLineSpacer = document.createElement('pre')
      newLineSpacer.id = `codeLine_${batchIndex}_${lineIndex}_s`
      newLineSpacer.innerHTML = ` `
      newLineSpacer.classList.add('hideit')
      newLineSpacer.classList.add('codeLineMarker')
      window[`codeLineWrapper${batchIndex}`].appendChild(newLineSpacer)
    })
  })
}

const handleNumberButtonClick = (event) => {
  s.currentLineSet = parseInt(event.target.id.split('_')[1])
  updateLines()
  updateButtons()
}

const addButtons = () => {
  for (let lineIndex = 1; lineIndex < lineSets.length - 1; lineIndex++) {
    const newButtonEl = document.createElement('button')
    newButtonEl.innerHTML = lineIndex
    newButtonEl.id = `stepButton_${lineIndex}`
    window.buttonWrapper.appendChild(newButtonEl)
    newButtonEl.addEventListener('click', handleNumberButtonClick)
  }
  const newButtonEl = document.createElement('button')
  newButtonEl.innerHTML = `Final`
  newButtonEl.id = `stepButton_${lineSets.length - 1}`
  window.buttonWrapper.appendChild(newButtonEl)
  newButtonEl.addEventListener('click', handleNumberButtonClick)
}

const addPreviousButton = () => {
  const previousButtonEl = document.createElement('button')
  previousButtonEl.id = 'previousSet'
  previousButtonEl.innerHTML = 'Previous'
  window.buttonWrapper.appendChild(previousButtonEl)
  window.previousSet.addEventListener('click', handlePreviousButtonClick)
}

const addNextButton = () => {
  const nextButtonEl = document.createElement('button')
  nextButtonEl.id = 'nextSet'
  nextButtonEl.innerHTML = 'Next'
  window.buttonWrapper.appendChild(nextButtonEl)
  window.nextSet.addEventListener('click', handleNextButtonClick)
}

const init = () => {
  loadSourceCode()
  makePreLines()
  makeBaseLines()
  updateLines()
  addPreviousButton()
  addButtons()
  addNextButton()
  updateButtons()
  window.nextSet.addEventListener('click', handleNextButtonClick)
}

document.addEventListener('DOMContentLoaded', init)