# Why doesn't the \`into\_string\` method be available directly under \`PathBuf\` even though it already exists in \`OsString\`?

**URL:** https://internals.rust-lang.org/t/why-doesnt-the-into-string-method-be-available-directly-under-pathbuf-even-though-it-already-exists-in-osstring/19935
**Category:** language design
**Created:** [November 27, 2023, 7:26pm UTC](https://internals.rust-lang.org/t/why-doesnt-the-into-string-method-be-available-directly-under-pathbuf-even-though-it-already-exists-in-osstring/19935 "2023-11-27T19:26:46Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![KSXGitHub](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ksxgithub/32/5582_2.png) [@KSXGitHub](https://internals.rust-lang.org/u/KSXGitHub)
#### Post date: [November 27, 2023, 7:26pm UTC](https://internals.rust-lang.org/t/why-doesnt-the-into-string-method-be-available-directly-under-pathbuf-even-though-it-already-exists-in-osstring/19935/1 "2023-11-27T19:26:46Z")

</div>

It's just annoying to have to convert a `PathBuf` to `OsString` before converting it to `String`.

---

<div class="post-metadata">

### Author: ![caellian](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/caellian/32/9680_2.png) [@caellian](https://internals.rust-lang.org/u/caellian)
#### Post date: [November 29, 2023, 7:09pm UTC](https://internals.rust-lang.org/t/why-doesnt-the-into-string-method-be-available-directly-under-pathbuf-even-though-it-already-exists-in-osstring/19935/2 "2023-11-29T19:09:05Z")

</div>

You can use:

- `PathBuf::to_str()` to access a `PathBut` as an `Option<&str>`
  - It's an `Option` because a system path can have non-utf8 characters and `&str` _is_ utf8.
  - If you want to handle non-utf8 strings use `OsString` directly.

- `PathBuf::to_string_lossy()`
  - If you want to access path for e.g. printing where you don't care that the `&str` you get out is an exact match (invalid characters won't be printed properly anyway).

Take a look at `OsString` documentation because it explains this in more detail.

`PathBuf::into_string()` isn't any more useful because with those two methods you still need two metod calls. `OsString::into_string()` returns a `Result<String, OsString>` where `Err` is for the non-utf8 case anyway. It's much more cost effective to check for encoding (with `to_str`) before copying an invalid String you won't be able to use later which is I guess why `into_string` doesn't exist.

---

<div class="post-metadata">

### Author: ![KSXGitHub](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ksxgithub/32/5582_2.png) [@KSXGitHub](https://internals.rust-lang.org/u/KSXGitHub)
#### Post date: [November 29, 2023, 7:22pm UTC](https://internals.rust-lang.org/t/why-doesnt-the-into-string-method-be-available-directly-under-pathbuf-even-though-it-already-exists-in-osstring/19935/3 "2023-11-29T19:22:28Z")

</div>

Not a real solution, I'm afraid.

I need `into_string` because it must be `into_string`:

- I need an owned string.
- I don't want to clone, both methods you suggest will clone.
- I need to error immediately on non-UTF8, no unnecessary task.

And given that the internal data of a `PathBuf` is just `OsString`, a `PathBuf::into_string` should be zero-cost.

---

<div class="post-metadata">

### Author: ![Jules-Bertholet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jules-bertholet/32/10671_2.png) [@Jules-Bertholet](https://internals.rust-lang.org/u/Jules-Bertholet)
#### Post date: [November 29, 2023, 7:29pm UTC](https://internals.rust-lang.org/t/why-doesnt-the-into-string-method-be-available-directly-under-pathbuf-even-though-it-already-exists-in-osstring/19935/4 "2023-11-29T19:29:24Z")

</div>

For small additions like this, you can submit an [API change proposal](https://std-dev-guide.rust-lang.org/development/feature-lifecycle.html).

---

<div class="post-metadata">

### Author: ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)
#### Post date: [November 29, 2023, 8:05pm UTC](https://internals.rust-lang.org/t/why-doesnt-the-into-string-method-be-available-directly-under-pathbuf-even-though-it-already-exists-in-osstring/19935/5 "2023-11-29T20:05:44Z")

</div>

I don't think there would be any major objections to

```rust
impl PathBuf {
    pub fn into_string(self) -> Result<String, PathBuf> {
        self.into_os_string().into_string().map_err(Into::into)
    }
}

```

as it's just a small helper to make already possible functionality easier to access. There may be some objection that this isn't an operation you "should" be doing (or that you "should" be using [camino](https://crates.io/crates/camino) for known-utf8 paths) and that the longer spelling pushes you towards doing the "right" thing more often, but it's already trivial to panic on invalid Unicode paths without one more `Result` returning method.

---

<div class="post-metadata">

### Author: ![KSXGitHub](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ksxgithub/32/5582_2.png) [@KSXGitHub](https://internals.rust-lang.org/u/KSXGitHub)
#### Post date: [November 29, 2023, 8:18pm UTC](https://internals.rust-lang.org/t/why-doesnt-the-into-string-method-be-available-directly-under-pathbuf-even-though-it-already-exists-in-osstring/19935/6 "2023-11-29T20:18:38Z")

</div>

Thank you for pointing me to camino, I will consider using it in my project. But for simple code with minimal dependencies, an `into_string` method is tremendously helpful.

I wouldn't buy the argument that it isn't what we should be doing, as a more expensive APIs (such as `to_str` or `to_string_lossy`) are currently more convenient to call.

---

<div class="post-metadata">

### Author: ![caellian](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/caellian/32/9680_2.png) [@caellian](https://internals.rust-lang.org/u/caellian)
#### Post date: [November 29, 2023, 8:28pm UTC](https://internals.rust-lang.org/t/why-doesnt-the-into-string-method-be-available-directly-under-pathbuf-even-though-it-already-exists-in-osstring/19935/7 "2023-11-29T20:28:52Z")

</div>

> a more expensive APIs (such as `to_str` or `to_string_lossy`) are currently more convenient to call.

`OsString::into_string()` does clone: [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7dd858d85241a4701572bdcece4305d5).

All 3 paths do basically the same amount of allocation and copying and just allow handling the error at different points. `OsString` and `String` have different guarantees about stored data and internal representation which means the data has to be moved to turn one into another. The only difference is `into_*` does explicit drop of self.

Use `path_buf.to_str().unwrap().to_string()` and create a utility trait extending `PathBuf` if you do it often enough that you'd save a lot of time by calling just a single method. If you're not going to keep the `OsString` if the operation fails (i.e. you unwrap), I believe `to_str` might be more clear.

---

<div class="post-metadata">

### Author: ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)
#### Post date: [November 29, 2023, 11:31pm UTC](https://internals.rust-lang.org/t/why-doesnt-the-into-string-method-be-available-directly-under-pathbuf-even-though-it-already-exists-in-osstring/19935/8 "2023-11-29T23:31:58Z")

</div>

> [@caellian](#):
>
> `OsString::into_string()` does clone: [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7dd858d85241a4701572bdcece4305d5).

This playground is wrong. `addr_of!(a)` is the address of the variable holding the `PathBuf` or `Result<String, …>`, respectively, not the address of the data.

```rust
use std::path::PathBuf;

pub fn main() {
    let a = PathBuf::from("/hello/world");
    println!("a: {:p}", a.as_os_str());
    let a = a.into_os_string().into_string();
    println!("b: {:p}", a.as_ref().unwrap().as_str());
}

```

```rust
a: 0x5609d34bf9d0
b: 0x5609d34bf9d0

```

* * *

> [@caellian](#):
>
> `OsString` and `String` have different guarantees about stored data and internal representation which means the data has to be moved to turn one into another.

`OsString`/`OsStr` (and `PathBuf`/`Path`) _do_ have guarantees of internal data representation: If their data is a valid unicode string, then it’s represented in UTF8, just like `String`/`str`, as evidenced by the existience of the `to_str` methods (for [Path[Buf]](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.to_str), for [OsStr[ing]](https://doc.rust-lang.org/std/ffi/struct.OsString.html#method.to_str)) returning a _ **borrowed** _ `str`, i.e. pointing to data that already existed in this format.

```rust
pub fn to_str(&self) -> Option<&str>

```

Though this method existing does not already prove conclusively that convering between owned `String` and `OsString`/`PathBuf` can happen without cloning, it _is_ the case that it can, as the fixed playground demonstrates. [For `OsString: From<String>`](https://doc.rust-lang.org/std/ffi/struct.OsString.html#impl-From%3CString%3E-for-OsString), this fact is even documented. Of course the other way _does_ still involve a linear scan, in order to validate all the data, which is also why the method is called `to_str`, not `as_str`, as it’s not a cheap constant-time operation. So converting `OsString` or `PathBuf` to `String` is not a lot cheaper than it would be to copy the data to a new allocation, but still, it doesn’t copy. (Converting the other way _is_ cheap though.)

---

<div class="post-metadata">

### Author: ![Jules-Bertholet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jules-bertholet/32/10671_2.png) [@Jules-Bertholet](https://internals.rust-lang.org/u/Jules-Bertholet)
#### Post date: [November 30, 2023, 12:08am UTC](https://internals.rust-lang.org/t/why-doesnt-the-into-string-method-be-available-directly-under-pathbuf-even-though-it-already-exists-in-osstring/19935/9 "2023-11-30T00:08:12Z")

</div>

> [@steffahn](#):
>
> `OsString`/`OsStr` (and `PathBuf`/`Path`) _do_ have guarantees of internal data representation

[To elaborate:](https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.as_encoded_bytes)

> The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit ASCII.

In practice, this unspecified encoding is currently [WTF-8](https://simonsapin.github.io/wtf-8/).

---

<div class="post-metadata">

### Author: ![chrisd](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/chrisd/32/7232_2.png) [@chrisd](https://internals.rust-lang.org/u/chrisd)
#### Post date: [November 30, 2023, 11:14pm UTC](https://internals.rust-lang.org/t/why-doesnt-the-into-string-method-be-available-directly-under-pathbuf-even-though-it-already-exists-in-osstring/19935/10 "2023-11-30T23:14:43Z")

</div>

> [@Jules-Bertholet](#):
>
> In practice, this unspecified encoding is currently [WTF-8](https://simonsapin.github.io/wtf-8/).

To be super clear, that's only true for Windows (and, to reiterate, it's not currently a stable guarantee). Other platforms may or may not have their own UTF-8 superset encoding. On posix systems this is usually arbitrary bytes which, for the sake of simplicity, are assumed to be UTF-8 like by default. You can do a manual encoding/decoding using (for example) the C locale or whatever.

Btw, Windows itself only really guarantees valid unicode for paths. That paths may not be validated is an implementation detail.

---

<div class="post-metadata">

### Author: ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)
#### Post date: [February 28, 2024, 11:15pm UTC](https://internals.rust-lang.org/t/why-doesnt-the-into-string-method-be-available-directly-under-pathbuf-even-though-it-already-exists-in-osstring/19935/11 "2024-02-28T23:15:26Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
