edlang/once_cell/index.html
2024-05-05 09:43:20 +00:00

290 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Overview"><title>once_cell - Rust</title><script> if (window.location.protocol !== "file:") document.write(`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2"><link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/FiraSans-Regular-018c141bf0843ffd.woff2"><link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/FiraSans-Medium-8f9a781e4970d388.woff2"><link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2"><link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2">`)</script><link rel="stylesheet" href="../static.files/normalize-76eba96aa4d2e634.css"><link rel="stylesheet" href="../static.files/rustdoc-e935ef01ae1c1829.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="once_cell" data-themes="" data-resource-suffix="" data-rustdoc-version="1.78.0 (9b00956e5 2024-04-29)" data-channel="1.78.0" data-search-js="search-42d8da7a6b9792c2.js" data-settings-js="settings-4313503d2e1961c2.js" ><script src="../static.files/storage-4c98445ec4002617.js"></script><script defer src="../crates.js"></script><script defer src="../static.files/main-12cf3b4f4f9dc36d.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-04d5337699b92874.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-16x16-8b506e7a72182f1c.png"><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-422f7d1d52889060.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-2c020d218678b618.svg"></head><body class="rustdoc mod crate"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../once_cell/index.html">once_cell</a><span class="version">1.19.0</span></h2></div><div class="sidebar-elems"><ul class="block">
<li><a id="all-types" href="all.html">All Items</a></li></ul><section><ul class="block"><li><a href="#modules">Modules</a></li></ul></section></div></nav><div class="sidebar-resizer"></div>
<main><div class="width-limiter"><nav class="sub"><form class="search-form"><span></span><div id="sidebar-button" tabindex="-1"><a href="../once_cell/all.html" title="show sidebar"></a></div><input class="search-input" name="search" aria-label="Run search in the documentation" autocomplete="off" spellcheck="false" placeholder="Click or press S to search, ? for more options…" type="search"><div id="help-button" tabindex="-1"><a href="../help.html" title="help">?</a></div><div id="settings-menu" tabindex="-1"><a href="../settings.html" title="settings"><img width="22" height="22" alt="Change settings" src="../static.files/wheel-7b819b6101059cd0.svg"></a></div></form></nav><section id="main-content" class="content"><div class="main-heading"><h1>Crate <a class="mod" href="#">once_cell</a><button id="copy-path" title="Copy item path to clipboard"><img src="../static.files/clipboard-7571035ce49a181d.svg" width="19" height="18" alt="Copy item path"></button></h1><span class="out-of-band"><a class="src" href="../src/once_cell/lib.rs.html#1-1412">source</a> · <button id="toggle-all-docs" title="collapse all docs">[<span>&#x2212;</span>]</button></span></div><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><h2 id="overview"><a class="doc-anchor" href="#overview">§</a>Overview</h2>
<p><code>once_cell</code> provides two new cell-like types, <a href="unsync/struct.OnceCell.html"><code>unsync::OnceCell</code></a> and
<a href="sync/struct.OnceCell.html"><code>sync::OnceCell</code></a>. A <code>OnceCell</code> might store arbitrary non-<code>Copy</code> types, can
be assigned to at most once and provides direct access to the stored
contents. The core API looks <em>roughly</em> like this (and theres much more
inside, read on!):</p>
<div class="example-wrap ignore"><a href="#" class="tooltip" title="This example is not tested"></a><pre class="rust rust-example-rendered"><code><span class="kw">impl</span>&lt;T&gt; OnceCell&lt;T&gt; {
<span class="kw">const fn </span>new() -&gt; OnceCell&lt;T&gt; { ... }
<span class="kw">fn </span>set(<span class="kw-2">&amp;</span><span class="self">self</span>, value: T) -&gt; <span class="prelude-ty">Result</span>&lt;(), T&gt; { ... }
<span class="kw">fn </span>get(<span class="kw-2">&amp;</span><span class="self">self</span>) -&gt; <span class="prelude-ty">Option</span>&lt;<span class="kw-2">&amp;</span>T&gt; { ... }
}</code></pre></div>
<p>Note that, like with <a href="https://doc.rust-lang.org/std/cell/struct.RefCell.html"><code>RefCell</code></a> and <a href="https://doc.rust-lang.org/std/sync/struct.Mutex.html"><code>Mutex</code></a>, the <code>set</code> method requires
only a shared reference. Because of the single assignment restriction <code>get</code>
can return a <code>&amp;T</code> instead of <code>Ref&lt;T&gt;</code> or <code>MutexGuard&lt;T&gt;</code>.</p>
<p>The <code>sync</code> flavor is thread-safe (that is, implements the <a href="https://doc.rust-lang.org/std/marker/trait.Sync.html"><code>Sync</code></a> trait),
while the <code>unsync</code> one is not.</p>
<h2 id="recipes"><a class="doc-anchor" href="#recipes">§</a>Recipes</h2>
<p><code>OnceCell</code> might be useful for a variety of patterns.</p>
<h3 id="safe-initialization-of-global-data"><a class="doc-anchor" href="#safe-initialization-of-global-data">§</a>Safe Initialization of Global Data</h3>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>std::{env, io};
<span class="kw">use </span>once_cell::sync::OnceCell;
<span class="attr">#[derive(Debug)]
</span><span class="kw">pub struct </span>Logger {
<span class="comment">// ...
</span>}
<span class="kw">static </span>INSTANCE: OnceCell&lt;Logger&gt; = OnceCell::new();
<span class="kw">impl </span>Logger {
<span class="kw">pub fn </span>global() -&gt; <span class="kw-2">&amp;</span><span class="lifetime">'static </span>Logger {
INSTANCE.get().expect(<span class="string">"logger is not initialized"</span>)
}
<span class="kw">fn </span>from_cli(args: env::Args) -&gt; <span class="prelude-ty">Result</span>&lt;Logger, std::io::Error&gt; {
<span class="comment">// ...
</span>}
}
<span class="kw">fn </span>main() {
<span class="kw">let </span>logger = Logger::from_cli(env::args()).unwrap();
INSTANCE.set(logger).unwrap();
<span class="comment">// use `Logger::global()` from now on
</span>}</code></pre></div>
<h3 id="lazy-initialized-global-data"><a class="doc-anchor" href="#lazy-initialized-global-data">§</a>Lazy Initialized Global Data</h3>
<p>This is essentially the <code>lazy_static!</code> macro, but without a macro.</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>std::{sync::Mutex, collections::HashMap};
<span class="kw">use </span>once_cell::sync::OnceCell;
<span class="kw">fn </span>global_data() -&gt; <span class="kw-2">&amp;</span><span class="lifetime">'static </span>Mutex&lt;HashMap&lt;i32, String&gt;&gt; {
<span class="kw">static </span>INSTANCE: OnceCell&lt;Mutex&lt;HashMap&lt;i32, String&gt;&gt;&gt; = OnceCell::new();
INSTANCE.get_or_init(|| {
<span class="kw">let </span><span class="kw-2">mut </span>m = HashMap::new();
m.insert(<span class="number">13</span>, <span class="string">"Spica"</span>.to_string());
m.insert(<span class="number">74</span>, <span class="string">"Hoyten"</span>.to_string());
Mutex::new(m)
})
}</code></pre></div>
<p>There are also the <a href="sync/struct.Lazy.html"><code>sync::Lazy</code></a> and <a href="unsync/struct.Lazy.html"><code>unsync::Lazy</code></a> convenience types to
streamline this pattern:</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>std::{sync::Mutex, collections::HashMap};
<span class="kw">use </span>once_cell::sync::Lazy;
<span class="kw">static </span>GLOBAL_DATA: Lazy&lt;Mutex&lt;HashMap&lt;i32, String&gt;&gt;&gt; = Lazy::new(|| {
<span class="kw">let </span><span class="kw-2">mut </span>m = HashMap::new();
m.insert(<span class="number">13</span>, <span class="string">"Spica"</span>.to_string());
m.insert(<span class="number">74</span>, <span class="string">"Hoyten"</span>.to_string());
Mutex::new(m)
});
<span class="kw">fn </span>main() {
<span class="macro">println!</span>(<span class="string">"{:?}"</span>, GLOBAL_DATA.lock().unwrap());
}</code></pre></div>
<p>Note that the variable that holds <code>Lazy</code> is declared as <code>static</code>, <em>not</em>
<code>const</code>. This is important: using <code>const</code> instead compiles, but works wrong.</p>
<h3 id="general-purpose-lazy-evaluation"><a class="doc-anchor" href="#general-purpose-lazy-evaluation">§</a>General purpose lazy evaluation</h3>
<p>Unlike <code>lazy_static!</code>, <code>Lazy</code> works with local variables.</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>once_cell::unsync::Lazy;
<span class="kw">fn </span>main() {
<span class="kw">let </span>ctx = <span class="macro">vec!</span>[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];
<span class="kw">let </span>thunk = Lazy::new(|| {
ctx.iter().sum::&lt;i32&gt;()
});
<span class="macro">assert_eq!</span>(<span class="kw-2">*</span>thunk, <span class="number">6</span>);
}</code></pre></div>
<p>If you need a lazy field in a struct, you probably should use <code>OnceCell</code>
directly, because that will allow you to access <code>self</code> during
initialization.</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>std::{fs, path::PathBuf};
<span class="kw">use </span>once_cell::unsync::OnceCell;
<span class="kw">struct </span>Ctx {
config_path: PathBuf,
config: OnceCell&lt;String&gt;,
}
<span class="kw">impl </span>Ctx {
<span class="kw">pub fn </span>get_config(<span class="kw-2">&amp;</span><span class="self">self</span>) -&gt; <span class="prelude-ty">Result</span>&lt;<span class="kw-2">&amp;</span>str, std::io::Error&gt; {
<span class="kw">let </span>cfg = <span class="self">self</span>.config.get_or_try_init(|| {
fs::read_to_string(<span class="kw-2">&amp;</span><span class="self">self</span>.config_path)
})<span class="question-mark">?</span>;
<span class="prelude-val">Ok</span>(cfg.as_str())
}
}</code></pre></div>
<h3 id="lazily-compiled-regex"><a class="doc-anchor" href="#lazily-compiled-regex">§</a>Lazily Compiled Regex</h3>
<p>This is a <code>regex!</code> macro which takes a string literal and returns an
<em>expression</em> that evaluates to a <code>&amp;'static Regex</code>:</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="macro">macro_rules!</span> regex {
(<span class="macro-nonterminal">$re</span>:literal $(,)<span class="question-mark">?</span>) =&gt; {{
<span class="kw">static </span>RE: once_cell::sync::OnceCell&lt;regex::Regex&gt; = once_cell::sync::OnceCell::new();
RE.get_or_init(|| regex::Regex::new(<span class="macro-nonterminal">$re</span>).unwrap())
}};
}</code></pre></div>
<p>This macro can be useful to avoid the “compile regex on every loop
iteration” problem.</p>
<h3 id="runtime-include_bytes"><a class="doc-anchor" href="#runtime-include_bytes">§</a>Runtime <code>include_bytes!</code></h3>
<p>The <code>include_bytes</code> macro is useful to include test resources, but it slows
down test compilation a lot. An alternative is to load the resources at
runtime:</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>std::path::Path;
<span class="kw">use </span>once_cell::sync::OnceCell;
<span class="kw">pub struct </span>TestResource {
path: <span class="kw-2">&amp;</span><span class="lifetime">'static </span>str,
cell: OnceCell&lt;Vec&lt;u8&gt;&gt;,
}
<span class="kw">impl </span>TestResource {
<span class="kw">pub const fn </span>new(path: <span class="kw-2">&amp;</span><span class="lifetime">'static </span>str) -&gt; TestResource {
TestResource { path, cell: OnceCell::new() }
}
<span class="kw">pub fn </span>bytes(<span class="kw-2">&amp;</span><span class="self">self</span>) -&gt; <span class="kw-2">&amp;</span>[u8] {
<span class="self">self</span>.cell.get_or_init(|| {
<span class="kw">let </span>dir = std::env::var(<span class="string">"CARGO_MANIFEST_DIR"</span>).unwrap();
<span class="kw">let </span>path = Path::new(dir.as_str()).join(<span class="self">self</span>.path);
std::fs::read(<span class="kw-2">&amp;</span>path).unwrap_or_else(|_err| {
<span class="macro">panic!</span>(<span class="string">"failed to load test resource: {}"</span>, path.display())
})
}).as_slice()
}
}
<span class="kw">static </span>TEST_IMAGE: TestResource = TestResource::new(<span class="string">"test_data/lena.png"</span>);
<span class="attr">#[test]
</span><span class="kw">fn </span>test_sobel_filter() {
<span class="kw">let </span>rgb: <span class="kw-2">&amp;</span>[u8] = TEST_IMAGE.bytes();
<span class="comment">// ...
</span>}</code></pre></div>
<h3 id="lateinit"><a class="doc-anchor" href="#lateinit">§</a><code>lateinit</code></h3>
<p><code>LateInit</code> type for delayed initialization. It is reminiscent of Kotlins
<code>lateinit</code> keyword and allows construction of cyclic data structures:</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>once_cell::sync::OnceCell;
<span class="kw">pub struct </span>LateInit&lt;T&gt; { cell: OnceCell&lt;T&gt; }
<span class="kw">impl</span>&lt;T&gt; LateInit&lt;T&gt; {
<span class="kw">pub fn </span>init(<span class="kw-2">&amp;</span><span class="self">self</span>, value: T) {
<span class="macro">assert!</span>(<span class="self">self</span>.cell.set(value).is_ok())
}
}
<span class="kw">impl</span>&lt;T&gt; Default <span class="kw">for </span>LateInit&lt;T&gt; {
<span class="kw">fn </span>default() -&gt; <span class="self">Self </span>{ LateInit { cell: OnceCell::default() } }
}
<span class="kw">impl</span>&lt;T&gt; std::ops::Deref <span class="kw">for </span>LateInit&lt;T&gt; {
<span class="kw">type </span>Target = T;
<span class="kw">fn </span>deref(<span class="kw-2">&amp;</span><span class="self">self</span>) -&gt; <span class="kw-2">&amp;</span>T {
<span class="self">self</span>.cell.get().unwrap()
}
}
<span class="attr">#[derive(Default)]
</span><span class="kw">struct </span>A&lt;<span class="lifetime">'a</span>&gt; {
b: LateInit&lt;<span class="kw-2">&amp;</span><span class="lifetime">'a </span>B&lt;<span class="lifetime">'a</span>&gt;&gt;,
}
<span class="attr">#[derive(Default)]
</span><span class="kw">struct </span>B&lt;<span class="lifetime">'a</span>&gt; {
a: LateInit&lt;<span class="kw-2">&amp;</span><span class="lifetime">'a </span>A&lt;<span class="lifetime">'a</span>&gt;&gt;
}
<span class="kw">fn </span>build_cycle() {
<span class="kw">let </span>a = A::default();
<span class="kw">let </span>b = B::default();
a.b.init(<span class="kw-2">&amp;</span>b);
b.a.init(<span class="kw-2">&amp;</span>a);
<span class="kw">let </span>_a = <span class="kw-2">&amp;</span>a.b.a.b.a;
}</code></pre></div>
<h2 id="comparison-with-std"><a class="doc-anchor" href="#comparison-with-std">§</a>Comparison with std</h2><div><table><thead><tr><th><code>!Sync</code> types</th><th>Access Mode</th><th>Drawbacks</th></tr></thead><tbody>
<tr><td><code>Cell&lt;T&gt;</code></td><td><code>T</code></td><td>requires <code>T: Copy</code> for <code>get</code></td></tr>
<tr><td><code>RefCell&lt;T&gt;</code></td><td><code>RefMut&lt;T&gt;</code> / <code>Ref&lt;T&gt;</code></td><td>may panic at runtime</td></tr>
<tr><td><code>unsync::OnceCell&lt;T&gt;</code></td><td><code>&amp;T</code></td><td>assignable only once</td></tr>
</tbody></table>
</div><div><table><thead><tr><th><code>Sync</code> types</th><th>Access Mode</th><th>Drawbacks</th></tr></thead><tbody>
<tr><td><code>AtomicT</code></td><td><code>T</code></td><td>works only with certain <code>Copy</code> types</td></tr>
<tr><td><code>Mutex&lt;T&gt;</code></td><td><code>MutexGuard&lt;T&gt;</code></td><td>may deadlock at runtime, may block the thread</td></tr>
<tr><td><code>sync::OnceCell&lt;T&gt;</code></td><td><code>&amp;T</code></td><td>assignable only once, may block the thread</td></tr>
</tbody></table>
</div>
<p>Technically, calling <code>get_or_init</code> will also cause a panic or a deadlock if
it recursively calls itself. However, because the assignment can happen only
once, such cases should be more rare than equivalents with <code>RefCell</code> and
<code>Mutex</code>.</p>
<h2 id="minimum-supported-rustc-version"><a class="doc-anchor" href="#minimum-supported-rustc-version">§</a>Minimum Supported <code>rustc</code> Version</h2>
<p>If only the <code>std</code>, <code>alloc</code>, or <code>race</code> features are enabled, MSRV will be
updated conservatively, supporting at least latest 8 versions of the compiler.
When using other features, like <code>parking_lot</code>, MSRV might be updated more
frequently, up to the latest stable. In both cases, increasing MSRV is <em>not</em>
considered a semver-breaking change and requires only a minor version bump.</p>
<h2 id="implementation-details"><a class="doc-anchor" href="#implementation-details">§</a>Implementation details</h2>
<p>The implementation is based on the
<a href="https://github.com/rust-lang-nursery/lazy-static.rs/"><code>lazy_static</code></a> and
<a href="https://github.com/indiv0/lazycell/"><code>lazy_cell</code></a> crates and
<a href="https://doc.rust-lang.org/std/sync/struct.Once.html"><code>std::sync::Once</code></a>. In some sense, <code>once_cell</code> just streamlines and unifies
those APIs.</p>
<p>To implement a sync flavor of <code>OnceCell</code>, this crates uses either a custom
re-implementation of <code>std::sync::Once</code> or <code>parking_lot::Mutex</code>. This is
controlled by the <code>parking_lot</code> feature (disabled by default). Performance
is the same for both cases, but the <code>parking_lot</code> based <code>OnceCell&lt;T&gt;</code> is
smaller by up to 16 bytes.</p>
<p>This crate uses <code>unsafe</code>.</p>
<h2 id="faq"><a class="doc-anchor" href="#faq">§</a>F.A.Q.</h2>
<p><strong>Should I use the sync or unsync flavor?</strong></p>
<p>Because Rust compiler checks thread safety for you, its impossible to
accidentally use <code>unsync</code> where <code>sync</code> is required. So, use <code>unsync</code> in
single-threaded code and <code>sync</code> in multi-threaded. Its easy to switch
between the two if code becomes multi-threaded later.</p>
<p>At the moment, <code>unsync</code> has an additional benefit that reentrant
initialization causes a panic, which might be easier to debug than a
deadlock.</p>
<p><strong>Does this crate support async?</strong></p>
<p>No, but you can use
<a href="https://crates.io/crates/async_once_cell"><code>async_once_cell</code></a> instead.</p>
<p><strong>Does this crate support <code>no_std</code>?</strong></p>
<p>Yes, but with caveats. <code>OnceCell</code> is a synchronization primitive which
<em>semantically</em> relies on blocking. <code>OnceCell</code> guarantees that at most one
<code>f</code> will be called to compute the value. If two threads of execution call
<code>get_or_init</code> concurrently, one of them has to wait.</p>
<p>Waiting fundamentally requires OS support. Execution environment needs to
understand who waits on whom to prevent deadlocks due to priority inversion.
You <em>could</em> make code to compile by blindly using pure spinlocks, but the
runtime behavior would be subtly wrong.</p>
<p>Given these constraints, <code>once_cell</code> provides the following options:</p>
<ul>
<li>The <code>race</code> module provides similar, but distinct synchronization primitive
which is compatible with <code>no_std</code>. With <code>race</code>, the <code>f</code> function can be
called multiple times by different threads, but only one thread will win
to install the value.</li>
<li><code>critical-section</code> feature (with a <code>-</code>, not <code>_</code>) uses <code>critical_section</code>
to implement blocking.</li>
</ul>
<p><strong>Can I bring my own mutex?</strong></p>
<p>There is <a href="https://crates.io/crates/generic_once_cell">generic_once_cell</a> to
allow just that.</p>
<p><strong>Should I use <code>std::cell::OnceCell</code>, <code>once_cell</code>, or <code>lazy_static</code>?</strong></p>
<p>If you can use <code>std</code> version (your MSRV is at least 1.70, and you dont need
extra features <code>once_cell</code> provides), use <code>std</code>. Otherwise, use <code>once_cell</code>.
Dont use <code>lazy_static</code>.</p>
<h2 id="related-crates"><a class="doc-anchor" href="#related-crates">§</a>Related crates</h2>
<ul>
<li>Most of this crates functionality is available in <code>std</code> starting with
Rust 1.70. See <code>std::cell::OnceCell</code> and <code>std::sync::OnceLock</code>.</li>
<li><a href="https://github.com/niklasf/double-checked-cell">double-checked-cell</a></li>
<li><a href="https://crates.io/crates/lazy-init">lazy-init</a></li>
<li><a href="https://crates.io/crates/lazycell">lazycell</a></li>
<li><a href="https://crates.io/crates/mitochondria">mitochondria</a></li>
<li><a href="https://crates.io/crates/lazy_static">lazy_static</a></li>
<li><a href="https://crates.io/crates/async_once_cell">async_once_cell</a></li>
<li><a href="https://crates.io/crates/generic_once_cell">generic_once_cell</a> (bring
your own mutex)</li>
</ul>
</div></details><h2 id="modules" class="section-header">Modules<a href="#modules" class="anchor">§</a></h2><ul class="item-table"><li><div class="item-name"><a class="mod" href="race/index.html" title="mod once_cell::race">race</a></div><div class="desc docblock-short">Thread-safe, non-blocking, “first one wins” flavor of <code>OnceCell</code>.</div></li><li><div class="item-name"><a class="mod" href="sync/index.html" title="mod once_cell::sync">sync</a></div><div class="desc docblock-short">Thread-safe, blocking version of <code>OnceCell</code>.</div></li><li><div class="item-name"><a class="mod" href="unsync/index.html" title="mod once_cell::unsync">unsync</a></div><div class="desc docblock-short">Single-threaded version of <code>OnceCell</code>.</div></li></ul></section></div></main></body></html>