Skip to main content

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}