1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use Cow;
use ;
use c_char;
/// A string parameter.
///
/// Medium-level API functions with string parameters accept all kinds of strings which can be
/// converted into this type, most notably `&CStr` and `&str`.
///
/// # Design
///
/// This is a wrapper around a `Cow<CStr>`.
///
/// ## Why C strings and not regular Rust strings?
///
/// We use a sort of C string because that perfectly accounts for the medium-level API's design goal
/// to be still as close to the original REAPER API as possible (while at the same time introducing
/// Rust's type safety). The C++ REAPER API generally expects C strings (`*const c_char`).
/// Fortunately UTF-8 encoded ones - which makes a character set conversion unnecessary.
///
/// ## Why `&CStr` and not `*const c_char`?
///
/// We don't use `*const c_char` directly because we want more type safety. We use `&CStr` instead
/// because in Rust that's the closest thing to a `*const c_char` (infact it's the same + some
/// additional guarantees). It's a reference instead of a pointer so we can assume it's neither
/// stale nor `null`. Also, the `&CStr` type gives us important guarantees, for example that there
/// are no intermediate zero bytes - which would make the string end abruptly in C world.
///
/// ## Why `Cow` and `ReaperStringArg`?
///
/// We don't use just a plain `&CStr` as parameter type because `&CStr` is not the regular string
/// type in Rust. It's much harder to create and use than `&str`. We want the API to be a pleasure
/// to use! That's the reason for adding `ReaperStringArg` and `Cow` to the mix. `Cow`is necessary
/// because we might need to own a possible conversion result (e.g. from `&str`). `ReaperStringArg`
/// is necessary to offer implicit conversions from regular Rust string types. Because medium-level
/// API functions take string parameters as `impl Into<ReaperStringArg>`, they *just work* with
/// regular Rust strings.
///
/// ## Performance considerations
///
/// A conversion from a regular Rust string is not entirely without cost because we need to check
/// for intermediate zero bytes and append a zero byte (which demands a copy if a borrowed string is
/// passed)! Therefore, if you want to be sure to not waste any performance and you can get cheap
/// access to a C string, just pass that one directly. Then there's no extra cost involved. In many
/// scenarios this is probably over optimization, but the point is, you *can* go the zero-cost way,
/// if you want.
///
/// In the *reaper-rs* code base you will find many examples that pass `c_str!("...")` to string
/// parameters. This macro from the [c_str_macro crate](https://crates.io/crates/c_str_macro)
/// creates static (UTF-8 encoded) `&CStr` literals, just as `"..."` creates static `&str` literals.
/// Because those literals are embedded in the binary itself, no heap-space allocation or conversion
/// is necessary at all. If you want, you can do the same with your literals.
;
// This is the most important conversion because it's the ideal case (zero-cost). For now we don't
// offer a conversion from `CString` (owned) because it could confuse consumers. They might start to
// think that string arguments are always consumed, which is not the case. If there's much demand,
// we can still add that later.
// This is the second most important conversion because we want consumers to be able to just pass a
// normal string literal.
// This conversion might appear somewhat unusual because it takes something *owned*. But that has a
// good reason: If there's a `String` which the consumer is okay to give away (move), this is
// good for performance because no copy needs to be made in order to convert this into a C string.
// By introducing this conversion, we want to encourage this scenario.