yash_env/system/c_string.rs
1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2026 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17//! Utilities for working with C-style strings ([`CStr`] and [`CString`]) in Rust
18//!
19//! This module provides abstractions for building and passing null-terminated
20//! arrays of pointers to C-style strings, primarily for [`Exec::execve`].
21//!
22//! This module defines two main traits:
23//!
24//! - [`AsCStrArray`]: unsafe low-level contract for types that can expose
25//! a pointer to a null-terminated array of pointers to C-style strings.
26//! - [`IntoCStrArray`]: ergonomic conversion trait used by [`Exec::execve`];
27//! accepts both native `AsCStrArray` implementors and convertible container
28//! types.
29//!
30//! And provides several struct implementations for different use cases:
31//!
32//! - [`CStrPtr`]: transparent wrapper for a raw pointer to an existing
33//! null-terminated C-string-pointer array.
34//! - [`BorrowedCStrs`]: owning pointer-array wrapper over borrowed string data,
35//! suitable when you already have `&CStr`/`&CString` values.
36//! - [`OwnedCStrs`]: owning wrapper that keeps both the strings and the pointer
37//! array alive together.
38//!
39//! In short, use [`BorrowedCStrs`] for borrowed inputs, [`OwnedCStrs`] for
40//! owned inputs, and [`CStrPtr`] only when interoperating with raw FFI data.
41//!
42//! [`Exec::execve`]: super::Exec::execve
43
44use std::ffi::{CStr, CString, c_char};
45use std::marker::PhantomData;
46
47/// Converts an iterator of `AsRef<CStr>` items into an iterator of pointers to
48/// C-style strings, with a null pointer appended at the end.
49fn null_terminated_pointers<I>(iter: I) -> impl Iterator<Item = *const c_char>
50where
51 I: IntoIterator,
52 I::Item: AsRef<CStr>,
53{
54 iter.into_iter()
55 .map(|s| s.as_ref().as_ptr())
56 .chain(std::iter::once(std::ptr::null()))
57}
58
59/// Dummy trait to prevent external implementations of `AsCStrArray` and
60/// `IntoCStrArray`
61trait Sealed {}
62
63/// Abstraction over an array of C-style strings, for arguments to [`Exec::execve`]
64///
65/// The native `execve` system call expects two arrays of C-style strings, which
66/// must be passed as pointers to null-terminated arrays of pointers to
67/// null-terminated byte strings. This trait abstracts over how ownership and
68/// lifetime of these arrays and strings are managed in Rust, allowing
69/// different types to be used as long as they can provide the required pointer
70/// to the array of C-style strings. The main requirement is that the array must
71/// be null-terminated and that the pointers in the array must point to valid
72/// C-style strings. Implementations of `Exec::execve` can then use this trait
73/// to accept different types of string arrays and to obtain the required
74/// pointers for the system call without needing to know the details of how the
75/// strings are stored or managed in Rust.
76///
77/// This trait is sealed to prevent external implementations, as the safety
78/// guarantees of the methods depend on the implementor upholding certain
79/// invariants about the pointers and the lifetime of the strings.
80///
81/// See also [`IntoCStrArray`] for types that can be converted into a
82/// `AsCStrArray`.
83///
84/// [`Exec::execve`]: super::Exec::execve
85#[allow(
86 clippy::missing_safety_doc,
87 reason = "users cannot implement sealed traits"
88)]
89#[expect(private_bounds, reason = "this trait is sealed")]
90// SAFETY: This trait is unsafe because improper implementations can lead to
91// undefined behavior when the pointers returned by `as_ptr` or `as_mut_ptr` are
92// used. The implementation of the `execve` function assumes that the array and
93// strings pointed to by these pointers are valid and remain unmodified while
94// they are in use. Interior mutability may break these assumptions, so the
95// implementor must ensure that no such mutations can occur.
96pub unsafe trait AsCStrArray: Sealed {
97 /// Returns a pointer to the array of C-style strings.
98 ///
99 /// The array must be null-terminated, i.e., the last pointer in the array
100 /// must be a null pointer. Each pointer in the array must point to a valid
101 /// C-style string (i.e., a null-terminated sequence of bytes). The array
102 /// and strings must remain valid and unmodified until `self` is mutated or
103 /// dropped. The caller must not mutate the array or strings through this
104 /// pointer.
105 fn as_ptr(&self) -> *const *const c_char;
106
107 /// Returns a pointer to the array of C-style strings.
108 ///
109 /// This method just returns the same pointer as [`as_ptr`](Self::as_ptr),
110 /// but with a different type signature. The mutability of the pointer is
111 /// only for matching the expected type signature of [`execve`] and does not
112 /// imply that the strings can actually be mutated through this pointer.
113 /// The caller must not mutate the array or strings through this pointer.
114 /// The array and strings must remain valid and unmodified until `self` is
115 /// mutated or dropped.
116 ///
117 /// [`execve`]: super::Exec::execve
118 #[inline(always)]
119 fn as_mut_ptr(&mut self) -> *const *mut c_char {
120 self.as_ptr().cast()
121 }
122
123 /// Constructs a `Vec<CString>` from the array of C-style strings.
124 ///
125 /// This method is a convenience for converting the array of C-style strings
126 /// into a more Rust-friendly format. It iterates over the pointers in the
127 /// array, converts each C-style string into a `CString`, and collects them
128 /// into a `Vec<CString>`. The returned `Vec<CString>` owns the new
129 /// allocations for the array and strings, so it is safe to use and modify
130 /// independently of `self`.
131 #[must_use]
132 fn to_vec(&self) -> Vec<CString> {
133 let mut vec = Vec::new();
134 let mut ptr = self.as_ptr();
135 loop {
136 // SAFETY: The `as_ptr` method guarantees that the returned pointer
137 // points to a null-terminated array of pointers to valid C-style
138 // strings, so it's safe to read from the pointer.
139 let c_str_ptr = unsafe { *ptr };
140 if c_str_ptr.is_null() {
141 break;
142 }
143 // SAFETY: Likewise, `c_str_ptr` points to a valid C-style string,
144 // so it's safe to create a `CStr` from it.
145 let c_str = unsafe { CStr::from_ptr(c_str_ptr) };
146 vec.push(c_str.to_owned());
147 // SAFETY: The `as_ptr` method guarantees that the array is
148 // null-terminated, so it's safe to advance the pointer until we
149 // reach the null pointer.
150 ptr = unsafe { ptr.add(1) };
151 }
152 vec
153 }
154}
155
156impl<T> Sealed for &T where T: Sealed + ?Sized {}
157unsafe impl<T> AsCStrArray for &T
158where
159 T: AsCStrArray + ?Sized,
160{
161 #[inline(always)]
162 fn as_ptr(&self) -> *const *const c_char {
163 (self as &T).as_ptr()
164 }
165}
166
167impl<T> Sealed for &mut T where T: Sealed + ?Sized {}
168unsafe impl<T> AsCStrArray for &mut T
169where
170 T: AsCStrArray + ?Sized,
171{
172 #[inline(always)]
173 fn as_ptr(&self) -> *const *const c_char {
174 (self as &T).as_ptr()
175 }
176}
177
178/// Simple wrapper to treat a raw pointer to an array of C-style strings as a [`AsCStrArray`]
179///
180/// This is useful for passing raw pointers obtained from other sources (e.g.,
181/// FFI) to functions that expect an `AsCStrArray`. However, it is the caller's
182/// responsibility to ensure that the pointer validly points to a
183/// null-terminated array of pointers to valid C-style strings, and that the
184/// array and strings remain valid for the required lifetime. This wrapper does
185/// not take ownership of the array or strings.
186///
187/// You should prefer [`BorrowedCStrs`] or [`OwnedCStrs`] over this type when
188/// possible, as they provide ownership and lifetime guarantees for the strings.
189#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
190#[repr(transparent)]
191pub struct CStrPtr(*const *const c_char);
192
193impl CStrPtr {
194 /// Create a new `CStrPtr` from a raw pointer to an array of C-style strings.
195 ///
196 /// The given pointer is directly returned by the [`as_ptr`](Self::as_ptr)
197 /// method.
198 ///
199 /// # Safety
200 ///
201 /// The caller must ensure that `ptr` points to a valid null-terminated
202 /// array of pointers to valid C-style strings. The array and strings must
203 /// remain valid and unmodified until the `CStrPtr` is dropped.
204 #[must_use]
205 pub unsafe fn new(ptr: *const *const c_char) -> Self {
206 Self(ptr)
207 }
208}
209
210impl Sealed for CStrPtr {}
211unsafe impl AsCStrArray for CStrPtr {
212 #[inline(always)]
213 fn as_ptr(&self) -> *const *const c_char {
214 self.0
215 }
216}
217
218/// An [`AsCStrArray`] backed by a [`Vec`] of borrowed [`CStr`]s
219///
220/// A `BorrowedCStrs<'a>` works as if it owns a `Vec<&'a CStr>`: it owns the
221/// allocation for the array of pointers, but the `&CStr`s themselves are
222/// borrowed from elsewhere. It is useful if you already have a collection of
223/// `&CStr`s (or [`CString`]s) you can borrow and want to pass them to a
224/// function that expects an `AsCStrArray`.
225///
226/// This struct implements [`FromIterator`], which can be used to create a
227/// `BorrowedCStrs` from existing C-style strings. The implementation can also
228/// be used via the [`IntoCStrArray`] trait.
229#[derive(Clone, Debug)]
230pub struct BorrowedCStrs<'a> {
231 pointers: Vec<*const c_char>,
232 phantom: PhantomData<Vec<&'a CStr>>,
233}
234
235impl<'a> Sealed for BorrowedCStrs<'a> {}
236unsafe impl<'a> AsCStrArray for BorrowedCStrs<'a> {
237 #[inline(always)]
238 fn as_ptr(&self) -> *const *const c_char {
239 self.pointers.as_ptr()
240 }
241}
242
243impl<'a, T> FromIterator<&'a T> for BorrowedCStrs<'a>
244where
245 T: AsRef<CStr> + ?Sized,
246{
247 #[inline(always)]
248 fn from_iter<I: IntoIterator<Item = &'a T>>(iter: I) -> Self {
249 let pointers = null_terminated_pointers(iter).collect();
250
251 // SAFETY: The `null_terminated_pointers` function creates a
252 // null-terminated array of pointers, and the pointers in the array
253 // point to C-style strings borrowed from the input iterator. The
254 // strings remain valid and unmodified for the lifetime of the
255 // `BorrowedCStrs` because they are borrowed immutably through the
256 // `PhantomData`.
257 unsafe { Self::from_vec(pointers) }
258 }
259}
260
261impl BorrowedCStrs<'_> {
262 /// Creates a new `BorrowedCStrs` from a `Vec<*const c_char>`.
263 ///
264 /// This function directly uses the given pointers as the content of the
265 /// `BorrowedCStrs`. The given pointers are returned by the
266 /// [`as_ptr`](Self::as_ptr) method.
267 ///
268 /// This function takes ownership of the given `Vec`, but not the strings
269 /// pointed to by the pointers.
270 ///
271 /// # Safety
272 ///
273 /// The caller must ensure that the pointers in the given `Vec` point to
274 /// valid C-style strings, and that the `Vec` is null-terminated (i.e., the
275 /// last pointer is a null pointer). The array and strings must remain valid
276 /// and unmodified for the required lifetime of the `BorrowedCStrs`.
277 #[inline(always)]
278 #[must_use]
279 pub unsafe fn from_vec(pointers: Vec<*const c_char>) -> Self {
280 Self {
281 pointers,
282 phantom: PhantomData,
283 }
284 }
285
286 /// Consumes the `BorrowedCStrs` and returns the owned array of pointers as
287 /// a `Vec<*const c_char>`.
288 ///
289 /// This function just returns the null-terminated array of pointers without
290 /// any ownership or lifetime guarantees for the strings. The caller must
291 /// ensure pointer validity if they intend to use the returned pointers.
292 #[inline(always)]
293 #[must_use]
294 pub fn into_vec(self) -> Vec<*const c_char> {
295 self.pointers
296 }
297}
298
299/// Consumes a `BorrowedCStrs` and extracts the owned array of pointers as a
300/// `Vec<*const c_char>`. (See [`BorrowedCStrs::into_vec`].)
301impl From<BorrowedCStrs<'_>> for Vec<*const c_char> {
302 #[inline(always)]
303 fn from(c_str_vec: BorrowedCStrs) -> Self {
304 c_str_vec.into_vec()
305 }
306}
307
308/// A [`AsCStrArray`] backed by a collection of owned strings
309///
310/// This struct is a counterpart to [`BorrowedCStrs`] that owns the strings
311/// themselves instead of borrowing them. `OwnedCStrs` owns both the allocation
312/// for the array of pointers and the `Vec` of owned strings, ensuring that the
313/// pointers remain valid for the lifetime of the `OwnedCStrs`.
314///
315/// The implementation of [`FromIterator`] allows you to create an `OwnedCStrs`
316/// from an iterator of `CString`s, which can be useful when you want to create
317/// an `AsCStrArray` from scratch and need to own the strings themselves.
318#[derive(Debug)]
319pub struct OwnedCStrs {
320 pointers: Vec<*const c_char>,
321 values: Vec<CString>,
322}
323
324impl Sealed for OwnedCStrs {}
325unsafe impl AsCStrArray for OwnedCStrs {
326 #[inline(always)]
327 fn as_ptr(&self) -> *const *const c_char {
328 self.pointers.as_ptr()
329 }
330}
331
332impl OwnedCStrs {
333 /// Creates a new `OwnedCStrs` from a `Vec<CString>`.
334 ///
335 /// The given `values` is stored in the `OwnedCStrs` for the lifetime of it
336 /// to keep the strings valid and unmodified. A null-terminated array of
337 /// pointers to the C-style strings is created from the `values` and stored
338 /// in the `OwnedCStrs` as well. The pointer to the array is returned by the
339 /// [`as_ptr`](Self::as_ptr) method.
340 #[must_use]
341 pub fn new(values: Vec<CString>) -> Self {
342 let pointers = null_terminated_pointers(&values).collect();
343
344 // SAFETY: `null_terminated_pointers` creates a null-terminated array,
345 // and the pointers in the array point to C-style strings borrowed from
346 // the `values`. The strings remain valid and unmodified for the
347 // lifetime of the `OwnedCStrs` because they are owned by it and not
348 // mutated through the pointers.
349 unsafe { Self::from_pointers_and_values(pointers, values) }
350 }
351
352 /// Creates a new `OwnedCStrs` from its inner components.
353 ///
354 /// This function directly uses the given `pointers` as the content of the
355 /// `OwnedCStrs`. The `pointers` is a null-terminated array of pointers to
356 /// C-style strings that is returned by the [`as_ptr`](Self::as_ptr) method.
357 /// The `values` is a `Vec<CString>` stored in the `OwnedCStrs` for the
358 /// lifetime of it.
359 ///
360 /// # Safety
361 ///
362 /// The caller must ensure that the pointers in the first `Vec` point to
363 /// valid C-style strings, and that the `Vec` is null-terminated (i.e., the
364 /// last pointer is a null pointer). The strings must remain valid and
365 /// unmodified for the lifetime of the `OwnedCStrs`.
366 ///
367 /// This function does not require that the `pointers` point to strings
368 /// contained in `values`. However, if the strings pointed to by `pointers`
369 /// are modified or dropped through other means while the `OwnedCStrs` is
370 /// alive, it may lead to undefined behavior when the pointers are used. If
371 /// the strings pointed to by `pointers` are not contained in `values`, you
372 /// should consider using [`BorrowedCStrs`] instead, as it supports
373 /// lifetime-based safety guarantees for the strings.
374 #[inline(always)]
375 #[must_use]
376 pub unsafe fn from_pointers_and_values(
377 pointers: Vec<*const c_char>,
378 values: Vec<CString>,
379 ) -> Self {
380 Self { pointers, values }
381 }
382
383 /// Consumes the `OwnedCStrs` and returns the owned array of pointers and values.
384 ///
385 /// This function just returns its inner components. The caller is
386 /// responsible for ensuring the validity of the returned pointers if they
387 /// intend to use them. If `self` was created by [`OwnedCStrs::new`], the
388 /// C-style strings pointed to by the returned pointers will be valid until
389 /// the returned `values` are mutated or dropped.
390 #[inline(always)]
391 #[must_use]
392 pub fn into_pointers_and_values(self) -> (Vec<*const c_char>, Vec<CString>) {
393 (self.pointers, self.values)
394 }
395}
396
397/// Clones an `OwnedCStrs` by cloning its owned strings and reconstructing the
398/// pointer array to point to the new strings.
399impl Clone for OwnedCStrs {
400 fn clone(&self) -> Self {
401 let strings = self.values.clone();
402 // The new pointers must point to the new strings, so we create a new
403 // `OwnedCStrs` from the cloned strings instead of just cloning the
404 // pointers.
405 Self::new(strings)
406 }
407
408 fn clone_from(&mut self, source: &Self) {
409 self.values.clone_from(&source.values);
410 // The new pointers must point to the new strings, so we need to
411 // reinitialize the content of the pointers array.
412 self.pointers.clear();
413 self.pointers.extend(null_terminated_pointers(&self.values));
414 }
415}
416
417/// Converts a `Vec<CString>` into an `OwnedCStrs`. (See [`OwnedCStrs::new`].)
418impl From<Vec<CString>> for OwnedCStrs {
419 #[inline(always)]
420 fn from(values: Vec<CString>) -> Self {
421 Self::new(values)
422 }
423}
424
425/// Creates an `OwnedCStrs` from an iterator of values that can be converted into `CString`s.
426impl<A> FromIterator<A> for OwnedCStrs
427where
428 A: Into<CString>,
429{
430 fn from_iter<I: IntoIterator<Item = A>>(iter: I) -> Self {
431 let values = iter.into_iter().map(Into::into).collect();
432 Self::new(values)
433 }
434}
435
436/// Types that can be converted into an [`AsCStrArray`]
437///
438/// This trait is the bound that is actually required for the [`Exec::execve`]
439/// function. By using this trait instead of [`AsCStrArray`] directly, `execve`
440/// can accept types that either implement `AsCStrArray` directly or can be
441/// converted into one, such as `Vec<CString>`. This reduces the need for users
442/// to manually convert their string collections into `BorrowedCStrs`s or other
443/// `AsCStrArray` implementors before passing them to `execve`.
444///
445/// Note that implementations of this trait may perform conversions that involve
446/// heap allocations, so users should be aware of the potential performance
447/// implications when using this trait with types that do not directly implement
448/// `AsCStrArray`.
449///
450/// This trait is currently sealed to avoid breakage in a possible future
451/// extension. Please open an
452/// [issue](https://github.com/magicant/yash-rs/issues) if you have a use case
453/// for implementing this trait for a type defined outside this crate and would
454/// like it to be unsealed.
455///
456/// [`Exec::execve`]: super::Exec::execve
457#[expect(private_bounds, reason = "this trait is sealed")]
458pub trait IntoCStrArray: Sealed {
459 /// The type that `self` can be converted into, which must implement `AsCStrArray`.
460 type CStrArray: AsCStrArray;
461
462 /// Converts `self` into a type that implements `AsCStrArray`.
463 #[must_use]
464 fn into_c_str_array(self) -> Self::CStrArray;
465}
466
467/// Any type that implements `AsCStrArray` can be converted into a
468/// `AsCStrArray` by identity conversion.
469impl<T: AsCStrArray> IntoCStrArray for T {
470 type CStrArray = Self;
471
472 #[inline(always)]
473 fn into_c_str_array(self) -> Self::CStrArray {
474 self
475 }
476}
477
478impl<T> Sealed for &[T] where T: AsRef<CStr> {}
479/// Converts a slice of `&CStr`s into a `BorrowedCStrs`. (See
480/// [`BorrowedCStrs::from_iter`].)
481impl<'a, T> IntoCStrArray for &'a [T]
482where
483 T: AsRef<CStr>,
484{
485 type CStrArray = BorrowedCStrs<'a>;
486
487 #[inline(always)]
488 fn into_c_str_array(self) -> BorrowedCStrs<'a> {
489 BorrowedCStrs::from_iter(self)
490 }
491}
492
493impl<T> Sealed for Vec<T> where T: Into<CString> {}
494/// Converts a `Vec<T>` into an `OwnedCStrs`. (See [`OwnedCStrs::from_iter`].)
495impl<T> IntoCStrArray for Vec<T>
496where
497 T: Into<CString>,
498{
499 type CStrArray = OwnedCStrs;
500
501 #[inline(always)]
502 fn into_c_str_array(self) -> OwnedCStrs {
503 OwnedCStrs::from_iter(self)
504 }
505}
506
507#[cfg(test)]
508mod tests {
509 use super::*;
510
511 fn make_c_strings(words: &[&str]) -> Vec<CString> {
512 words.iter().map(|s| CString::new(*s).unwrap()).collect()
513 }
514
515 #[test]
516 fn as_c_str_array_to_vec() {
517 let strings = make_c_strings(&["foo", "bar", "baz"]);
518 let owned = OwnedCStrs::new(strings.clone());
519 assert_eq!(owned.to_vec(), strings);
520
521 // Empty array should round-trip to an empty Vec
522 let empty = OwnedCStrs::new(vec![]);
523 assert_eq!(empty.to_vec(), Vec::<CString>::new());
524 }
525
526 #[test]
527 fn borrowed_c_strs_from_iterator() {
528 let strings = make_c_strings(&["alpha", "beta"]);
529
530 // Collect borrows into BorrowedCStrs
531 let borrowed: BorrowedCStrs = strings.iter().collect();
532
533 // Content round-trips correctly
534 assert_eq!(borrowed.to_vec(), strings);
535
536 // The pointers in the array must actually borrow from `strings`
537 let ptr = borrowed.as_ptr();
538 unsafe {
539 assert_eq!(*ptr, strings[0].as_ptr());
540 assert_eq!(*ptr.add(1), strings[1].as_ptr());
541 // Array is null-terminated
542 assert!((*ptr.add(2)).is_null());
543 }
544 }
545
546 #[test]
547 fn owned_c_strs_new() {
548 let strings = make_c_strings(&["hello", "world"]);
549
550 let owned = OwnedCStrs::new(strings.clone());
551
552 // Content is preserved
553 assert_eq!(owned.to_vec(), strings);
554
555 // Pointers must be self-consistent (point into the owned values)
556 let (pointers, values) = owned.into_pointers_and_values();
557 for (i, value) in values.iter().enumerate() {
558 assert_eq!(pointers[i], value.as_ptr());
559 }
560 assert!(pointers[values.len()].is_null());
561 }
562
563 #[test]
564 fn owned_c_strs_clone() {
565 let strings = make_c_strings(&["foo", "bar"]);
566 let owned = OwnedCStrs::new(strings.clone());
567
568 let cloned = owned.clone();
569
570 // Cloned content matches the original
571 assert_eq!(cloned.to_vec(), strings);
572
573 // The clone must own its own copy of the strings, so the pointers
574 // must point to different memory than the original's pointers.
575 let orig_ptr = owned.as_ptr();
576 let clone_ptr = cloned.as_ptr();
577 unsafe {
578 assert_ne!(orig_ptr, clone_ptr);
579 assert_ne!(*orig_ptr, *clone_ptr);
580 assert_ne!(*orig_ptr.add(1), *clone_ptr.add(1));
581 }
582 }
583
584 #[test]
585 fn owned_c_strs_clone_from() {
586 let strings1 = make_c_strings(&["one"]);
587 let strings2 = make_c_strings(&["two", "three"]);
588 let source = OwnedCStrs::new(strings2.clone());
589 let mut dest = OwnedCStrs::new(strings1);
590
591 dest.clone_from(&source);
592
593 assert_eq!(dest.to_vec(), strings2);
594
595 // dest's pointers must be self-consistent (point into dest's own values)
596 let (pointers, values) = dest.into_pointers_and_values();
597 for (i, value) in values.iter().enumerate() {
598 assert_eq!(pointers[i], value.as_ptr());
599 }
600 assert!(pointers[values.len()].is_null());
601 }
602
603 #[test]
604 fn owned_c_strs_from_iterator() {
605 let strings = make_c_strings(&["x", "y", "z"]);
606
607 // Collect owned CStrings via the FromIterator impl
608 let owned: OwnedCStrs = strings.iter().cloned().collect();
609
610 assert_eq!(owned.to_vec(), strings);
611
612 // The pointers in the array must be self-consistent (point into the owned values)
613 let (pointers, values) = owned.into_pointers_and_values();
614 for (i, value) in values.iter().enumerate() {
615 assert_eq!(pointers[i], value.as_ptr());
616 }
617 assert!(pointers[values.len()].is_null());
618 }
619}