So I've written up what the title says. Obviously, I'm looking for speed, safety and ease of use. To achieve the perfect ratio I've made some functions straight away crash if the input is wrong (in debug mode only, otherwise UD). The design is inspired by the C++ counterpart. Will be glad to hear other opinions. (It's a lot of code, so bear with me.)dynstring.h#ifndef DYNSTRING_H#define DYNSTRING_H#include <stddef.h>#include <stdbool.h>typedef struct {    char* data;    size_t size;} string;extern bool string_create(string*);extern bool string_create_n(string*, size_t);extern bool string_create_from_raw(string*, const char*);extern void string_destroy(string*);extern bool string_copy(string*, string);extern bool string_copy_from_raw(string*, const char*);extern void string_set(string*, size_t, char);extern char string_get(string, size_t);extern char* string_front(string);extern char* string_back(string);extern char* string_to_raw(string);extern size_t string_size(string);extern bool   string_empty(string);extern void string_erase(string*, size_t, size_t);extern bool string_push_back(string*, char);extern bool string_push_front(string*, char);extern bool string_pop_back(string*);extern bool string_pop_front(string*);extern bool string_append(string*, string);extern bool string_append_raw(string*, const char*);extern bool string_substr(string, size_t, size_t, string*);extern void string_swap(string*, string*);extern int  string_compare(string, string);extern size_t string_find(string, char);extern size_t string_find_substr(string, const char*);#endifdynstring.c#include dynstring.h#include <stdbool.h>#include <assert.h>#include <stddef.h>#include <stdlib.h>#include <string.h>bool string_create(string* str){    assert(str);    // allocate 1 for '\0'    str->data = malloc(sizeof(char));    if (!str->data) {        return false;    }    str->size = 0;    str->data[0] = '\0';    return true;}bool string_create_n(string* str, size_t size){    assert(str);    if (size == 0) {        return string_create(str);    }    // one extra for '\0'    str->data = malloc(sizeof(char) * size + 1);    if (!str->data) {        return false;    }    str->size = size;    str->data[str->size] = '\0';    return true;}bool string_create_from_raw(string* str, const char* from){    assert(str && from);    if (!string_create_n(str, strlen(from))) {        return false;    }    memcpy(str->data, from, sizeof(char) * str->size);    return true;}void string_destroy(string* str){    assert(str);    free(str->data);}bool string_copy(string* dest, string src){    assert(dest);    if (dest->size >= src.size) {        memcpy(dest->data, src.data, sizeof(char) * src.size + 1);    }    else    {        char* temp = malloc(sizeof(char) * src.size + 1);        if (!temp) {            return false;        }        memcpy(temp, src.data, sizeof(char) * src.size + 1);        free(dest->data);        dest->data = temp;    }    dest->size = src.size;    return true;}bool string_copy_from_raw(string* dest, const char* src){    assert(dest && src);    string str;    if (!string_create_from_raw(&str, src)) {        return false;    }    string_swap(&str, dest);    string_destroy(&str);    return true;}void string_set(string* str, size_t i, char c){    assert(str && str->size > i);    str->data[i] = c;}char string_get(string str, size_t i){    assert(str.size > i);    return str.data[i];}char* string_front(string str) {    return str.data;}char* string_back(string str) {    return str.data + str.size - 1;}// same as string_front// exists only because it looks better// to say puts(string_to_raw(str));char* string_to_raw(string str) {    return str.data;}size_t string_size(string str) {    return str.size;}bool string_empty(string str) {    return str.size;}// erases characters in range [i, i+n] includingvoid string_erase(string* str, size_t i, size_t n){    assert(str && str->size > i);    if (i + n > str->size) {        str->size = i;    }    else if (n != 0)    {        memcpy(str->data + i,                str->data + i + n,                sizeof(char) * str->size - i + n);        str->size -= n;    }    str->data[str->size] = 0;}bool string_push_back(string* str, char ch){    assert(str);    string temp;    if (!string_create_n(&temp, str->size + 1)) {        return false;    }    memcpy(temp.data, str->data, sizeof(char) * str->size);    temp.data[temp.size - 1] = ch;    string_swap(&temp, str);    string_destroy(&temp);    return true;}bool string_push_front(string* str, char ch){    assert(str);    string temp;    if (!string_create_n(&temp, str->size + 1)) {        return false;    }    memcpy(temp.data + 1, str->data, sizeof(char) * str->size);    temp.data[0] = ch;    string_swap(&temp, str);    string_destroy(&temp);    return true;}bool string_pop_back(string* str){    assert(str && str->size != 0);    --str->size;    str->data[str->size] = 0;    return true;}bool string_pop_front(string* str){    assert(str && str->size != 0);    string temp;    if (!string_create_from_raw(&temp, str->data + 1)) {        return false;    }    string_destroy(str);    string_swap(&temp, str);    return true;}bool string_append(string* str, string toapp){    assert(str);    string temp;    if (!string_create_n(&temp, str->size + toapp.size)) {        return false;    }    memcpy(temp.data, str->data, str->size);    memcpy(temp.data + str->size, toapp.data, toapp.size);    string_swap(&temp, str);    string_destroy(&temp);    return true;}bool string_append_raw(string* str, const char* toapp){    assert(str && toapp);    size_t len = strlen(toapp);    string temp;    if (!string_create_n(&temp, str->size + len)) {        return false;    }    memcpy(temp.data, str->data, str->size);    memcpy(temp.data + str->size, toapp, len);    string_swap(&temp, str);    string_destroy(&temp);    return true;}// puts the substring from index i to index j into 'to'bool string_substr(string from, size_t i, size_t j, string* to){    assert(i < j && j < from.size && to);    string temp;    if (!string_create_n(&temp, j - i + 1)) {        return false;    }    memcpy(temp.data, from.data + i, j - i + 1);    string_swap(&temp, to);    string_destroy(&temp);    return true;}void string_swap(string* lhs, string* rhs){    assert(lhs && rhs);    char* data = lhs->data;    lhs->data = rhs->data;    rhs->data = data;    size_t size = lhs->size;    lhs->size = rhs->size;    rhs->size = size;}int string_compare(string lhs, string rhs) {    return strcmp(lhs.data, rhs.data);}size_t string_find(string str, char c){    char* find = strchr(str.data, c);    if (!find) {        return (size_t)-1;    }    return (size_t)(find - str.data);}size_t string_find_substr(string str, const char* tofind){    assert(tofind);    char* find = strstr(str.data, tofind);    if (!find) {        return (size_t)-1;    }    return (size_t)(find - str.data);}
A dynamic string interface in C
c;strings;c11