Add Rust constructor for pinned CxxString on stack
diff --git a/src/cxx_string.rs b/src/cxx_string.rs
index 273905e..7a66b47 100644
--- a/src/cxx_string.rs
+++ b/src/cxx_string.rs
@@ -1,12 +1,18 @@
+use crate::actually_private::Private;
 use alloc::borrow::Cow;
 use alloc::string::String;
 use core::fmt::{self, Debug, Display};
 use core::marker::{PhantomData, PhantomPinned};
+use core::mem::MaybeUninit;
 use core::pin::Pin;
 use core::slice;
 use core::str::{self, Utf8Error};
 
 extern "C" {
+    #[link_name = "cxxbridge05$cxx_string$init"]
+    fn string_init(this: &mut MaybeUninit<CxxString>, ptr: *const u8, len: usize);
+    #[link_name = "cxxbridge05$cxx_string$destroy"]
+    fn string_destroy(this: &mut MaybeUninit<CxxString>);
     #[link_name = "cxxbridge05$cxx_string$data"]
     fn string_data(this: &CxxString) -> *const u8;
     #[link_name = "cxxbridge05$cxx_string$length"]
@@ -31,7 +37,53 @@
     _pinned: PhantomData<PhantomPinned>,
 }
 
+/// Construct a C++ std::string on the Rust stack.
+///
+/// # Syntax
+///
+/// In statement position:
+///
+/// ```
+/// # use cxx::let_cxx_string;
+/// # let expression = "";
+/// let_cxx_string!(var = expression);
+/// ```
+///
+/// The `expression` may have any type that implements `AsRef<[u8]>`. Commonly
+/// it will be a string literal, but for example `&[u8]` and `String` would work
+/// as well.
+///
+/// The macro expands to something resembling `let $var: Pin<&mut CxxString> =
+/// /*???*/;`. The resulting [`Pin`] can be deref'd to `&CxxString` as needed.
+///
+/// # Example
+///
+/// ```
+/// use cxx::{let_cxx_string, CxxString};
+///
+/// fn f(s: &CxxString) {/* ... */}
+///
+/// fn main() {
+///     let_cxx_string!(s = "example");
+///     f(&s);
+/// }
+/// ```
+#[macro_export]
+macro_rules! let_cxx_string {
+    ($var:ident = $value:expr $(,)?) => {
+        let mut $var = $crate::private::StackString::new();
+        #[allow(unused_mut, unused_unsafe)]
+        let mut $var = unsafe { $var.init($value) };
+    };
+}
+
 impl CxxString {
+    /// `CxxString` is not constructible via `new`. Instead, use the
+    /// [`let_cxx_string!`] macro.
+    pub fn new<T: Private>() -> Self {
+        unreachable!()
+    }
+
     /// Returns the length of the string in bytes.
     ///
     /// Matches the behavior of C++ [std::string::size][size].
@@ -128,3 +180,35 @@
         self.as_bytes() == other.as_bytes()
     }
 }
+
+#[doc(hidden)]
+#[repr(C)]
+pub struct StackString {
+    // Static assertions in cxx.cc validate that this is large enough and
+    // aligned enough.
+    space: MaybeUninit<[*const (); 8]>,
+}
+
+impl StackString {
+    pub fn new() -> Self {
+        StackString {
+            space: MaybeUninit::uninit(),
+        }
+    }
+
+    pub unsafe fn init(&mut self, value: impl AsRef<[u8]>) -> Pin<&mut CxxString> {
+        let value = value.as_ref();
+        let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>();
+        string_init(this, value.as_ptr(), value.len());
+        Pin::new_unchecked(&mut *this.as_mut_ptr())
+    }
+}
+
+impl Drop for StackString {
+    fn drop(&mut self) {
+        unsafe {
+            let this = &mut *self.space.as_mut_ptr().cast::<MaybeUninit<CxxString>>();
+            string_destroy(this);
+        }
+    }
+}