# Frank's Rust String Class

**URL:** <https://users.rust-lang.org/t/franks-rust-string-class/1972>\
**Category:** uncategorized\
**Created:** [June 30, 2015, 12:40pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972 "2015-06-30T12:40:44Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![frank\_gerlach](https://avatars.discourse-cdn.com/v4/letter/f/f05b48/32.png) [@frank\_gerlach](https://users.rust-lang.org/u/frank_gerlach)\
**Post date:** [June 30, 2015, 12:40pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/1 "2015-06-30T12:40:44Z")

</div>

Hello fellow Rust-users,  
as I felt the built-in String types are a royal pain in the backside, I created my own String class. It surely does not make sense to convert from UTF8 to UTF32 back and forth. After all this language is supposed to be efficient, if I am not mistaken.

Please feel free to download from

[http://frankgerlach.d-n-s.name/RustStringClass.txt](http://frankgerlach.d-n-s.name/RustStringClass.txt)

Also feel free to modify and use in any way you want to. Needless to say it is free of charge and no patents have been filed (if such an insane thing would be possible).

Frank Gerlach  
Tailfingen  
Germany

---

<div class="post-metadata">

**Author:** ![DanielKeep](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/danielkeep/32/1713_2.png) [@DanielKeep](https://users.rust-lang.org/u/DanielKeep)\
**Post date:** [June 30, 2015, 12:44pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/2 "2015-06-30T12:44:18Z")

</div>

... but why would you want to use UTF-32, anyway?

---

<div class="post-metadata">

**Author:** ![frank\_gerlach](https://avatars.discourse-cdn.com/v4/letter/f/f05b48/32.png) [@frank\_gerlach](https://users.rust-lang.org/u/frank_gerlach)\
**Post date:** [June 30, 2015, 12:45pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/3 "2015-06-30T12:45:10Z")

</div>

Well, it seems to be the canonic character representation.

What would you suggest instead ?

---

<div class="post-metadata">

**Author:** ![frank\_gerlach](https://avatars.discourse-cdn.com/v4/letter/f/f05b48/32.png) [@frank\_gerlach](https://users.rust-lang.org/u/frank_gerlach)\
**Post date:** [June 30, 2015, 12:46pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/4 "2015-06-30T12:46:30Z")

</div>

Also, could I parametrize the character type (like C++ template classes ?)

---

<div class="post-metadata">

**Author:** ![DanielKeep](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/danielkeep/32/1713_2.png) [@DanielKeep](https://users.rust-lang.org/u/DanielKeep)\
**Post date:** [June 30, 2015, 12:47pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/5 "2015-06-30T12:47:12Z")

</div>

`char` isn't a character; it's a mis-named code point. Using UTF-32 doesn't gain you anything; it's _still_ a variable-width encoding of text, so you _still_ can't do O(1) indexing.

It just makes almost every string take more memory and tricks people into thinking they don't have to worry about composite characters.

> [@frank\_gerlach](#):
>
> Also, could I parametrize the character type (like C++ template classes ?)

Yes, although I'm still not sure what the point would be. UTF-8 is about the only sane encoding for text, outside of specialised domains.

---

<div class="post-metadata">

**Author:** ![bluss](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/bluss/32/4051_2.png) [@bluss](https://users.rust-lang.org/u/bluss)\
**Post date:** [June 30, 2015, 12:49pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/6 "2015-06-30T12:49:05Z")

</div>

The Rust `String` will be more efficient than yours for a lot of use cases too, especially when you input or output `UTF-8`.

Very brief notes on the code:

- `Vec::truncate` doesn't release any memory, use `shrink_to_fit` if you want that.
- It's very uncommon to have an API in another language than english, but I like it, it's funny, and I'm pro using your natural language when you want, so I don't see the problem if you want it.
- Use rust's `#[test]` feature to easily add tests! Cargo will help to easily run it too.

---

<div class="post-metadata">

**Author:** ![frank\_gerlach](https://avatars.discourse-cdn.com/v4/letter/f/f05b48/32.png) [@frank\_gerlach](https://users.rust-lang.org/u/frank_gerlach)\
**Post date:** [June 30, 2015, 12:50pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/7 "2015-06-30T12:50:17Z")

</div>

I am in the process of building an http server. ASCII would suffice here.

And the built-in String stuff is really bad when trying to write a CLEAN lexer.

Maybe "I am holding it wrong", but Rust surely needs a String class on par with Java or STL.

---

<div class="post-metadata">

**Author:** ![frank\_gerlach](https://avatars.discourse-cdn.com/v4/letter/f/f05b48/32.png) [@frank\_gerlach](https://users.rust-lang.org/u/frank_gerlach)\
**Post date:** [June 30, 2015, 12:51pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/8 "2015-06-30T12:51:47Z")

</div>

Thanks for this feedback, I will change the code accordingly.

---

<div class="post-metadata">

**Author:** ![DanielKeep](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/danielkeep/32/1713_2.png) [@DanielKeep](https://users.rust-lang.org/u/DanielKeep)\
**Post date:** [June 30, 2015, 12:54pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/9 "2015-06-30T12:54:27Z")

</div>

> [@frank\_gerlach](#):
>
> I am in the process of building an http server. ASCII would suffice here.

I thought HTTP used `latin-1` or something, not ASCII. In either case, this means your string is going to use _four times_ more memory than is necessary.

> [@frank\_gerlach](#):
>
> And the built-in String stuff is really bad when trying to write a CLEAN lexer.

How so? I've found that Rust's string type really only lacks a few niceties, notably popping a grapheme from the front. That was why I wrote the [`strcursor`](https://crates.io/crates/strcursor) crate.

> [@frank\_gerlach](#):
>
> Maybe "I am holding it wrong", but Rust surely needs a String class on par with Java or STL.

Again, what is it missing? Just don't say "getting `char` at an index" because that's almost _never_ a meaningful or reasonable thing to do.

And Java's a bad example: last I checked, it used UTF-16, and its `char` isn't even a complete code point! I don't know if it checks for unpaired surrogates...

---

<div class="post-metadata">

**Author:** ![frank\_gerlach](https://avatars.discourse-cdn.com/v4/letter/f/f05b48/32.png) [@frank\_gerlach](https://users.rust-lang.org/u/frank_gerlach)\
**Post date:** [June 30, 2015, 12:55pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/10 "2015-06-30T12:55:58Z")

</div>

Yeah, maybe it is just a matter of "Java habit" and "C++ habit"....

---

<div class="post-metadata">

**Author:** ![BurntSushi](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/burntsushi/32/135_2.png) [@BurntSushi](https://users.rust-lang.org/u/BurntSushi)\
**Post date:** [June 30, 2015, 12:56pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/11 "2015-06-30T12:56:17Z")

</div>

Rust's `String` type is by far the most conveniently correct string abstraction I've ever used.

---

<div class="post-metadata">

**Author:** ![frank\_gerlach](https://avatars.discourse-cdn.com/v4/letter/f/f05b48/32.png) [@frank\_gerlach](https://users.rust-lang.org/u/frank_gerlach)\
**Post date:** [June 30, 2015, 1:03pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/12 "2015-06-30T13:03:25Z")

</div>

Well in the real world (automotive industry) we do funny things with strings. For example the FIN and the VIN, which identifies your car.

In some cases it is useful to access this string in a random-access manner.

E.g. I only want the product line "222", then I might not be interested to read the first three characters, because I already know the entire data set belongs to my company....

Or many other funny encodings inside the FIN.

Copied from somewhere:  
z.B. WDB2110061A123456

WDB211 : Baureihe 211 (neue E-Klasse)  
006 : Motornummer  
1 : Linkslenker (2=Rechtslenker)  
A : Herstellungsplatz (A is Sindelfingen)  
123456 : Folgenummer

---

<div class="post-metadata">

**Author:** ![frank\_gerlach](https://avatars.discourse-cdn.com/v4/letter/f/f05b48/32.png) [@frank\_gerlach](https://users.rust-lang.org/u/frank_gerlach)\
**Post date:** [June 30, 2015, 1:05pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/13 "2015-06-30T13:05:35Z")

</div>

Or in a language parser - the Rust way of comparing a string with a literal seems to be rather costly, because of the conversion into a Vec.

But as I say, maybe I haven't yet got a good documentation and I simply "hold it wrong"...

---

<div class="post-metadata">

**Author:** ![DanielKeep](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/danielkeep/32/1713_2.png) [@DanielKeep](https://users.rust-lang.org/u/DanielKeep)\
**Post date:** [June 30, 2015, 1:10pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/14 "2015-06-30T13:10:11Z")

</div>

> [@frank\_gerlach](#):
>
> I might not be interested to read the first three characters, because I already know the entire data set belongs to my company....

Then use `&s[3..]`. But this doesn't sound like Unicode text; it sounds like mixed text and structured data. You should probably be processing this as an array of `u8`s or something. Or an array of `Ascii` (there's a crate for this floating around somewhere, I think). Or decoding into a structured type.

It sounds very much like you're using the wrong type for the job, then complaining that the type is wrong.

> [@frank\_gerlach](#):
>
> the Rust way of comparing a string with a literal seems to be rather costly, because of the conversion into a Vec

This is just _completely false_.

For your own sake, you should make sure you understand the Rust standard library _before_ you expend a huge effort re-implementing it to solve non-existent problems. 🙂

---

<div class="post-metadata">

**Author:** ![bluss](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/bluss/32/4051_2.png) [@bluss](https://users.rust-lang.org/u/bluss)\
**Post date:** [June 30, 2015, 1:12pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/15 "2015-06-30T13:12:39Z")

</div>

Comparing a string with a literal or a `Vec<u8>` with a byte string literal should absolutely not be costly. I usually think that our String, for example, might be a little bit harder to use exactly because we care so much about providing low-overhead operations.

Comparing a string to a literal involves no conversion into vec or any costly operation except for the string data comparison itself:

```
let s = "hallo".to_string();
let is_equal = s == "hallo";

```

The only conversion happening here is that `s` is converted from `String` to `&str` which is zero cost in practice, `&str` is a lightweight string view type, which allows simple and low overhead operations with substrings. Unlike C we can create substrings easily with a simple location reference and length, because we don't use string terminators (the zero byte in C).

---

<div class="post-metadata">

**Author:** ![frank\_gerlach](https://avatars.discourse-cdn.com/v4/letter/f/f05b48/32.png) [@frank\_gerlach](https://users.rust-lang.org/u/frank_gerlach)\
**Post date:** [June 30, 2015, 1:19pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/16 "2015-06-30T13:19:38Z")

</div>

Ok, thanks for educating me 😄

One cannot shake off 20 years of C, Pascal and Java easily...

---

<div class="post-metadata">

**Author:** ![DanielKeep](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/danielkeep/32/1713_2.png) [@DanielKeep](https://users.rust-lang.org/u/DanielKeep)\
**Post date:** [June 30, 2015, 1:22pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/17 "2015-06-30T13:22:31Z")

</div>

Don't worry about it; every language I've ever used (including Rust) perpetuates at least some misconceptions about how text works. It's a minor miracle _anything_ involving text works at all.

---

<div class="post-metadata">

**Author:** ![frank\_gerlach](https://avatars.discourse-cdn.com/v4/letter/f/f05b48/32.png) [@frank\_gerlach](https://users.rust-lang.org/u/frank_gerlach)\
**Post date:** [June 30, 2015, 1:23pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/18 "2015-06-30T13:23:06Z")

</div>

Well, I think I effectively did what you said: I created a random-access-array type of some sorts 😄

Certainly for HTTP a Oktet would suffice and UTF32 is excessive.

The FIN actually is even less characters than ASCII and can be compressed even more (which we did in order to squeeze more data sets into RAM).

---

<div class="post-metadata">

**Author:** ![Andi\_K](https://avatars.discourse-cdn.com/v4/letter/a/49beb7/32.png) [@Andi\_K](https://users.rust-lang.org/u/Andi_K)\
**Post date:** [July 1, 2015, 2:36pm UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/19 "2015-07-01T14:36:08Z")

</div>

Hey Frank, i used your FIN example form my own learning session.

I guess you will love the result:

```rust
#[derive(Debug)] // damit println!("{:?}", fin); geht
#[allow(non_snake_case)] // meckert sonst über Großschreibung
struct FIN {
    pub Firma: [u8; 3],
    pub Baureihe: u16,
    pub Motornummer: u8,
    pub Linkslenker: bool, // (false = Rechtslenker)
    pub Herstellungsplatz: char,
    pub Folgenummer: u32,
}

#[allow(non_snake_case)]
#[allow(dead_code)]
impl FIN {
    fn from_str(fin: &str) -> FIN {
        fn sub_str<'a>(str: &'a str, offset: &mut u8, size: u8) -> &'a str {
            let start = *offset;
            *offset += size;
            &str[start as usize .. *offset as usize]
        }

        use std::str::FromStr;
        
        let mut offset = 0;

        let firma: [u8; 3] = match fin.len() {
            17 => {
// fin.bytes().take(3).into_array()
                offset = 3;

                let mut b = fin.bytes();
                [b.next().unwrap(),
                 b.next().unwrap(),
                 b.next().unwrap()]
            }
            14 => {
                [87, 66, 68] // "WBD"
            }
            _ => { panic!(format!("Ungültiger FIN '{}'", fin))}
        };

        // collections::slice::SliceConcatExt => unstable
        // let s = fin.chars().take(3).concat().parse::<u16>();
        
        FIN {
            Firma: firma,
            Baureihe: FromStr::from_str(sub_str(fin, &mut offset, 3)).unwrap(),
            Motornummer: FromStr::from_str(sub_str(fin, &mut offset, 3)).unwrap(),
            Linkslenker: sub_str(fin, &mut offset, 1).chars().next().unwrap() == '1',
            Herstellungsplatz: sub_str(fin, &mut offset, 1).chars().next().unwrap(),
            Folgenummer: FromStr::from_str(sub_str(fin, &mut offset, 6)).unwrap(),
        }
    }
    
    fn als_Rechtslenker(&self) -> FIN {
            FIN {Linkslenker: false, .. *self}
    }

    fn to_string(&self) -> String {
        use std::ascii::AsciiExt;
        
        let mut buf = String::with_capacity(17);
        let firma = String::from_utf8(self.Firma.to_ascii_uppercase()).unwrap();
        buf.push_str(&format!("{}{:03}{:03}", firma, self.Baureihe, self.Motornummer));
        buf.push(if self.Linkslenker {'1'} else {'2'});
        buf.push_str(&format!("{}{:6}", self.Herstellungsplatz, self.Folgenummer));
        buf
    }
}

use std::fmt;
use std::collections::HashMap;

    impl fmt::Display for FIN {
        #[allow(unused_must_use)]
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let hp = { //HerstellungsPlätze
                let mut p: HashMap<char, &'static str> = HashMap::new();
                p.insert('A', "Sindelfingen");
                p.insert('B', "Buxdehude");
                p
            };
            
            f.write_str(if self.Linkslenker { "Lenker: links" } else { "Lenker: für Engländer ;)" });
            f.write_str(&format!("\nHerstellungsplatz: {}",
                    hp.get(&(self.Herstellungsplatz)).unwrap_or(&"Unbekannt")));
            
            fmt::Display::fmt("\n", f)
        }
    }

#[test]
fn test_it() {
        let fin = FIN {
        Firma: b"wbd".to_owned(),
        Baureihe: 211,
        Motornummer: 6,
        Linkslenker: true,
        Herstellungsplatz: 'A',
        Folgenummer: 123456,
    };

    assert_eq!("WBD2110061A123456", fin.to_string());
    assert_eq!("WBD2120061B123789", FIN::from_str("WBD2120061B123789").to_string());
}

fn main() {
    let fin1 = FIN {
        Firma: b"WBD".to_owned(),
        Baureihe: 211,
        Motornummer: 6,
        Linkslenker: true,
        Herstellungsplatz: 'A',
        Folgenummer: 123456,
    };
    
    let fin2 = FIN {Herstellungsplatz: 'C', Folgenummer: fin1.Folgenummer + 1, .. fin1};
    
    println!("{}:", fin1.to_string());
    println!("{}", fin1);
    println!("{}", fin1.als_Rechtslenker());

    println!("\n{}:", fin2.to_string());
    println!("{:?}", fin2);
    println!("{}", fin2);

    println!("\n{:?}", FIN::from_str("2220032B234567"));
}

```

---

<div class="post-metadata">

**Author:** ![frank\_gerlach](https://avatars.discourse-cdn.com/v4/letter/f/f05b48/32.png) [@frank\_gerlach](https://users.rust-lang.org/u/frank_gerlach)\
**Post date:** [July 2, 2015, 9:07am UTC](https://users.rust-lang.org/t/franks-rust-string-class/1972/20 "2015-07-02T09:07:52Z")

</div>

Hi Andy,  
it seems you are quite deep inside Rust 😄

I currently don't have the time to look into your code, but thanks nevertheless.

I will have a detailed look at it later.

[Next page](https://users.rust-lang.org/t/franks-rust-string-class/1972.md?page=2)
