minor fixes to the debug printout of the main example
This commit is contained in:
429
book/4_pin.html
429
book/4_pin.html
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js">
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
@@ -32,11 +32,11 @@
|
||||
|
||||
|
||||
</head>
|
||||
<body class="light">
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "";
|
||||
var default_theme = "light";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
@@ -60,8 +60,11 @@
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
document.body.className = theme;
|
||||
document.querySelector('html').className = theme + ' js';
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
@@ -77,8 +80,8 @@
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li><a href="4_pin.html" class="active"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
|
||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="expanded affix "><a href="introduction.html">Introduction</a></li><li class="expanded "><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li class="expanded "><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li class="expanded "><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li class="expanded "><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li class="expanded "><a href="4_pin.html" class="active"><strong aria-hidden="true">5.</strong> Pin</a></li><li class="expanded "><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li class="expanded "><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="expanded affix "><a href="conclusion.html">Conclusion and exercises</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
@@ -240,37 +243,37 @@ you see, this works as expected:</p>
|
||||
println!("a: {}, b: {}", test2.a(), test2.b());
|
||||
|
||||
}
|
||||
# use std::pin::Pin;
|
||||
# #[derive(Debug)]
|
||||
# struct Test {
|
||||
# a: String,
|
||||
# b: *const String,
|
||||
# }
|
||||
#
|
||||
# impl Test {
|
||||
# fn new(txt: &str) -> Self {
|
||||
# let a = String::from(txt);
|
||||
# Test {
|
||||
# a,
|
||||
# b: std::ptr::null(),
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# // We need an `init` method to actually set our self-reference
|
||||
# fn init(&mut self) {
|
||||
# let self_ref: *const String = &self.a;
|
||||
# self.b = self_ref;
|
||||
# }
|
||||
#
|
||||
# fn a(&self) -> &str {
|
||||
# &self.a
|
||||
# }
|
||||
#
|
||||
# fn b(&self) -> &String {
|
||||
# unsafe {&*(self.b)}
|
||||
# }
|
||||
# }
|
||||
</code></pre></pre>
|
||||
<span class="boring">use std::pin::Pin;
|
||||
</span><span class="boring">#[derive(Debug)]
|
||||
</span><span class="boring">struct Test {
|
||||
</span><span class="boring"> a: String,
|
||||
</span><span class="boring"> b: *const String,
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">impl Test {
|
||||
</span><span class="boring"> fn new(txt: &str) -> Self {
|
||||
</span><span class="boring"> let a = String::from(txt);
|
||||
</span><span class="boring"> Test {
|
||||
</span><span class="boring"> a,
|
||||
</span><span class="boring"> b: std::ptr::null(),
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> // We need an `init` method to actually set our self-reference
|
||||
</span><span class="boring"> fn init(&mut self) {
|
||||
</span><span class="boring"> let self_ref: *const String = &self.a;
|
||||
</span><span class="boring"> self.b = self_ref;
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn a(&self) -> &str {
|
||||
</span><span class="boring"> &self.a
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn b(&self) -> &String {
|
||||
</span><span class="boring"> unsafe {&*(self.b)}
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>In our main method we first instantiate two instances of <code>Test</code> and print out
|
||||
the value of the fields on <code>test1</code>. We get what we'd expect:</p>
|
||||
<pre><code class="language-rust ignore">a: test1, b: test1
|
||||
@@ -290,36 +293,36 @@ which <code>test1</code> is pointing to with the data stored at the memory locat
|
||||
println!("a: {}, b: {}", test2.a(), test2.b());
|
||||
|
||||
}
|
||||
# use std::pin::Pin;
|
||||
# #[derive(Debug)]
|
||||
# struct Test {
|
||||
# a: String,
|
||||
# b: *const String,
|
||||
# }
|
||||
#
|
||||
# impl Test {
|
||||
# fn new(txt: &str) -> Self {
|
||||
# let a = String::from(txt);
|
||||
# Test {
|
||||
# a,
|
||||
# b: std::ptr::null(),
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# fn init(&mut self) {
|
||||
# let self_ref: *const String = &self.a;
|
||||
# self.b = self_ref;
|
||||
# }
|
||||
#
|
||||
# fn a(&self) -> &str {
|
||||
# &self.a
|
||||
# }
|
||||
#
|
||||
# fn b(&self) -> &String {
|
||||
# unsafe {&*(self.b)}
|
||||
# }
|
||||
# }
|
||||
</code></pre></pre>
|
||||
<span class="boring">use std::pin::Pin;
|
||||
</span><span class="boring">#[derive(Debug)]
|
||||
</span><span class="boring">struct Test {
|
||||
</span><span class="boring"> a: String,
|
||||
</span><span class="boring"> b: *const String,
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">impl Test {
|
||||
</span><span class="boring"> fn new(txt: &str) -> Self {
|
||||
</span><span class="boring"> let a = String::from(txt);
|
||||
</span><span class="boring"> Test {
|
||||
</span><span class="boring"> a,
|
||||
</span><span class="boring"> b: std::ptr::null(),
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn init(&mut self) {
|
||||
</span><span class="boring"> let self_ref: *const String = &self.a;
|
||||
</span><span class="boring"> self.b = self_ref;
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn a(&self) -> &str {
|
||||
</span><span class="boring"> &self.a
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn b(&self) -> &String {
|
||||
</span><span class="boring"> unsafe {&*(self.b)}
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>Naively, we could think that what we should get a debug print of <code>test1</code> two
|
||||
times like this</p>
|
||||
<pre><code class="language-rust ignore">a: test1, b: test1
|
||||
@@ -346,36 +349,36 @@ be tied to the lifetime of <code>test2</code> anymore.</p>
|
||||
println!("a: {}, b: {}", test2.a(), test2.b());
|
||||
|
||||
}
|
||||
# use std::pin::Pin;
|
||||
# #[derive(Debug)]
|
||||
# struct Test {
|
||||
# a: String,
|
||||
# b: *const String,
|
||||
# }
|
||||
#
|
||||
# impl Test {
|
||||
# fn new(txt: &str) -> Self {
|
||||
# let a = String::from(txt);
|
||||
# Test {
|
||||
# a,
|
||||
# b: std::ptr::null(),
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# fn init(&mut self) {
|
||||
# let self_ref: *const String = &self.a;
|
||||
# self.b = self_ref;
|
||||
# }
|
||||
#
|
||||
# fn a(&self) -> &str {
|
||||
# &self.a
|
||||
# }
|
||||
#
|
||||
# fn b(&self) -> &String {
|
||||
# unsafe {&*(self.b)}
|
||||
# }
|
||||
# }
|
||||
</code></pre></pre>
|
||||
<span class="boring">use std::pin::Pin;
|
||||
</span><span class="boring">#[derive(Debug)]
|
||||
</span><span class="boring">struct Test {
|
||||
</span><span class="boring"> a: String,
|
||||
</span><span class="boring"> b: *const String,
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">impl Test {
|
||||
</span><span class="boring"> fn new(txt: &str) -> Self {
|
||||
</span><span class="boring"> let a = String::from(txt);
|
||||
</span><span class="boring"> Test {
|
||||
</span><span class="boring"> a,
|
||||
</span><span class="boring"> b: std::ptr::null(),
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn init(&mut self) {
|
||||
</span><span class="boring"> let self_ref: *const String = &self.a;
|
||||
</span><span class="boring"> self.b = self_ref;
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn a(&self) -> &str {
|
||||
</span><span class="boring"> &self.a
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn b(&self) -> &String {
|
||||
</span><span class="boring"> unsafe {&*(self.b)}
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>That shouldn't happen. There is no serious error yet, but as you can imagine
|
||||
it's easy to create serious bugs using this code.</p>
|
||||
<p>I created a diagram to help visualize what's going on:</p>
|
||||
@@ -442,42 +445,42 @@ we'll show in a second.</p>
|
||||
println!("a: {}, b: {}", Test::a(test1.as_ref()), Test::b(test1.as_ref()));
|
||||
println!("a: {}, b: {}", Test::a(test2.as_ref()), Test::b(test2.as_ref()));
|
||||
}
|
||||
# use std::pin::Pin;
|
||||
# use std::marker::PhantomPinned;
|
||||
#
|
||||
# #[derive(Debug)]
|
||||
# struct Test {
|
||||
# a: String,
|
||||
# b: *const String,
|
||||
# _marker: PhantomPinned,
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl Test {
|
||||
# fn new(txt: &str) -> Self {
|
||||
# let a = String::from(txt);
|
||||
# Test {
|
||||
# a,
|
||||
# b: std::ptr::null(),
|
||||
# // This makes our type `!Unpin`
|
||||
# _marker: PhantomPinned,
|
||||
# }
|
||||
# }
|
||||
# fn init<'a>(self: Pin<&'a mut Self>) {
|
||||
# let self_ptr: *const String = &self.a;
|
||||
# let this = unsafe { self.get_unchecked_mut() };
|
||||
# this.b = self_ptr;
|
||||
# }
|
||||
#
|
||||
# fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
# &self.get_ref().a
|
||||
# }
|
||||
#
|
||||
# fn b<'a>(self: Pin<&'a Self>) -> &'a String {
|
||||
# unsafe { &*(self.b) }
|
||||
# }
|
||||
# }
|
||||
</code></pre></pre>
|
||||
<span class="boring">use std::pin::Pin;
|
||||
</span><span class="boring">use std::marker::PhantomPinned;
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">#[derive(Debug)]
|
||||
</span><span class="boring">struct Test {
|
||||
</span><span class="boring"> a: String,
|
||||
</span><span class="boring"> b: *const String,
|
||||
</span><span class="boring"> _marker: PhantomPinned,
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">impl Test {
|
||||
</span><span class="boring"> fn new(txt: &str) -> Self {
|
||||
</span><span class="boring"> let a = String::from(txt);
|
||||
</span><span class="boring"> Test {
|
||||
</span><span class="boring"> a,
|
||||
</span><span class="boring"> b: std::ptr::null(),
|
||||
</span><span class="boring"> // This makes our type `!Unpin`
|
||||
</span><span class="boring"> _marker: PhantomPinned,
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring"> fn init<'a>(self: Pin<&'a mut Self>) {
|
||||
</span><span class="boring"> let self_ptr: *const String = &self.a;
|
||||
</span><span class="boring"> let this = unsafe { self.get_unchecked_mut() };
|
||||
</span><span class="boring"> this.b = self_ptr;
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
</span><span class="boring"> &self.get_ref().a
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn b<'a>(self: Pin<&'a Self>) -> &'a String {
|
||||
</span><span class="boring"> unsafe { &*(self.b) }
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>Now, if we try to pull the same trick which got us in to trouble the last time
|
||||
you'll get a compilation error.</p>
|
||||
<pre><pre class="playpen"><code class="language-rust compile_fail">pub fn main() {
|
||||
@@ -493,42 +496,42 @@ you'll get a compilation error.</p>
|
||||
std::mem::swap(test1.get_mut(), test2.get_mut());
|
||||
println!("a: {}, b: {}", Test::a(test2.as_ref()), Test::b(test2.as_ref()));
|
||||
}
|
||||
# use std::pin::Pin;
|
||||
# use std::marker::PhantomPinned;
|
||||
#
|
||||
# #[derive(Debug)]
|
||||
# struct Test {
|
||||
# a: String,
|
||||
# b: *const String,
|
||||
# _marker: PhantomPinned,
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl Test {
|
||||
# fn new(txt: &str) -> Self {
|
||||
# let a = String::from(txt);
|
||||
# Test {
|
||||
# a,
|
||||
# b: std::ptr::null(),
|
||||
# // This makes our type `!Unpin`
|
||||
# _marker: PhantomPinned,
|
||||
# }
|
||||
# }
|
||||
# fn init<'a>(self: Pin<&'a mut Self>) {
|
||||
# let self_ptr: *const String = &self.a;
|
||||
# let this = unsafe { self.get_unchecked_mut() };
|
||||
# this.b = self_ptr;
|
||||
# }
|
||||
#
|
||||
# fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
# &self.get_ref().a
|
||||
# }
|
||||
#
|
||||
# fn b<'a>(self: Pin<&'a Self>) -> &'a String {
|
||||
# unsafe { &*(self.b) }
|
||||
# }
|
||||
# }
|
||||
</code></pre></pre>
|
||||
<span class="boring">use std::pin::Pin;
|
||||
</span><span class="boring">use std::marker::PhantomPinned;
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">#[derive(Debug)]
|
||||
</span><span class="boring">struct Test {
|
||||
</span><span class="boring"> a: String,
|
||||
</span><span class="boring"> b: *const String,
|
||||
</span><span class="boring"> _marker: PhantomPinned,
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">impl Test {
|
||||
</span><span class="boring"> fn new(txt: &str) -> Self {
|
||||
</span><span class="boring"> let a = String::from(txt);
|
||||
</span><span class="boring"> Test {
|
||||
</span><span class="boring"> a,
|
||||
</span><span class="boring"> b: std::ptr::null(),
|
||||
</span><span class="boring"> // This makes our type `!Unpin`
|
||||
</span><span class="boring"> _marker: PhantomPinned,
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring"> fn init<'a>(self: Pin<&'a mut Self>) {
|
||||
</span><span class="boring"> let self_ptr: *const String = &self.a;
|
||||
</span><span class="boring"> let this = unsafe { self.get_unchecked_mut() };
|
||||
</span><span class="boring"> this.b = self_ptr;
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
</span><span class="boring"> &self.get_ref().a
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn b<'a>(self: Pin<&'a Self>) -> &'a String {
|
||||
</span><span class="boring"> unsafe { &*(self.b) }
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
<p>As you see from the error you get by running the code the type system prevents
|
||||
us from swapping the pinned pointers.</p>
|
||||
<blockquote>
|
||||
@@ -549,43 +552,43 @@ after it's initialized like this:</p>
|
||||
mem::swap(&mut test1, &mut test2);
|
||||
println!("Not self referential anymore: {:?}", test1.b);
|
||||
}
|
||||
# use std::pin::Pin;
|
||||
# use std::marker::PhantomPinned;
|
||||
# use std::mem;
|
||||
#
|
||||
# #[derive(Debug)]
|
||||
# struct Test {
|
||||
# a: String,
|
||||
# b: *const String,
|
||||
# _marker: PhantomPinned,
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl Test {
|
||||
# fn new(txt: &str) -> Self {
|
||||
# let a = String::from(txt);
|
||||
# Test {
|
||||
# a,
|
||||
# b: std::ptr::null(),
|
||||
# // This makes our type `!Unpin`
|
||||
# _marker: PhantomPinned,
|
||||
# }
|
||||
# }
|
||||
# fn init<'a>(self: Pin<&'a mut Self>) {
|
||||
# let self_ptr: *const String = &self.a;
|
||||
# let this = unsafe { self.get_unchecked_mut() };
|
||||
# this.b = self_ptr;
|
||||
# }
|
||||
#
|
||||
# fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
# &self.get_ref().a
|
||||
# }
|
||||
#
|
||||
# fn b<'a>(self: Pin<&'a Self>) -> &'a String {
|
||||
# unsafe { &*(self.b) }
|
||||
# }
|
||||
# }
|
||||
</code></pre></pre>
|
||||
<span class="boring">use std::pin::Pin;
|
||||
</span><span class="boring">use std::marker::PhantomPinned;
|
||||
</span><span class="boring">use std::mem;
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">#[derive(Debug)]
|
||||
</span><span class="boring">struct Test {
|
||||
</span><span class="boring"> a: String,
|
||||
</span><span class="boring"> b: *const String,
|
||||
</span><span class="boring"> _marker: PhantomPinned,
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">impl Test {
|
||||
</span><span class="boring"> fn new(txt: &str) -> Self {
|
||||
</span><span class="boring"> let a = String::from(txt);
|
||||
</span><span class="boring"> Test {
|
||||
</span><span class="boring"> a,
|
||||
</span><span class="boring"> b: std::ptr::null(),
|
||||
</span><span class="boring"> // This makes our type `!Unpin`
|
||||
</span><span class="boring"> _marker: PhantomPinned,
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring"> fn init<'a>(self: Pin<&'a mut Self>) {
|
||||
</span><span class="boring"> let self_ptr: *const String = &self.a;
|
||||
</span><span class="boring"> let this = unsafe { self.get_unchecked_mut() };
|
||||
</span><span class="boring"> this.b = self_ptr;
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
</span><span class="boring"> &self.get_ref().a
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> fn b<'a>(self: Pin<&'a Self>) -> &'a String {
|
||||
</span><span class="boring"> unsafe { &*(self.b) }
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}
|
||||
</span></code></pre></pre>
|
||||
</blockquote>
|
||||
<h2><a class="header" href="#pinning-to-the-heap" id="pinning-to-the-heap">Pinning to the heap</a></h2>
|
||||
<p>For completeness let's remove some unsafe and the need for an <code>init</code> method
|
||||
@@ -902,6 +905,18 @@ we want to be able to safely borrow across <code>yield/await</code> points.</p>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playpen_line_numbers = true;
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.playpen_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
Reference in New Issue
Block a user