Skip to content

Commit f3a3ac0

Browse files
authored
[Rust] Look through typedefs to pass borrowed strings as &str (#1102)
wit-bindgen already knows to special-case passing strings by reference and pass them as `&str` instead of naively passing them as `&String` and requiring callers to have owned `Strings`. Implement this behavior for type aliases of strings as well, so that in code like this: ```wit type my-string = string; foo: func(x: my-string); ``` the argument is `&str` instead of `&MyString` which is effectively `&String`. And similar for lists. This comes up in several functions in wasi-http; for example, in the bindings for the `Fields::append` function, it enables this change: ```diff @@ -5075,8 +5075,8 @@ pub mod wasi { /// `field-value` are syntactically invalid. pub fn append( &self, - name: &FieldName, - value: &FieldValue, + name: &str, + value: &[u8], ) -> Result<(), HeaderError> { unsafe { #[repr(align(1))] ``` where `FieldName` and `FieldValue` are defined as: ```wit pub type FieldKey = _rt::String; pub type FieldName = FieldKey; pub type FieldValue = _rt::Vec<u8>; ```
1 parent d1387cc commit f3a3ac0

File tree

2 files changed

+77
-1
lines changed

2 files changed

+77
-1
lines changed

crates/rust/src/interface.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,30 @@ macro_rules! {macro_name} {{
11131113
}
11141114

11151115
fn print_ty(&mut self, ty: &Type, mode: TypeMode) {
1116+
// If we have a typedef of a string or a list, the typedef is an alias
1117+
// for `String` or `Vec<T>`. If this is a borrow, instead of borrowing
1118+
// them as `&String` or `&Vec<T>`, use `&str` or `&[T]` so that callers
1119+
// don't need to create owned copies.
1120+
if let Type::Id(id) = ty {
1121+
let id = dealias(self.resolve, *id);
1122+
let typedef = &self.resolve.types[id];
1123+
match &typedef.kind {
1124+
TypeDefKind::Type(Type::String) => {
1125+
if let Some(lt) = mode.lifetime {
1126+
self.print_borrowed_str(lt);
1127+
return;
1128+
}
1129+
}
1130+
TypeDefKind::List(element) => {
1131+
if mode.lifetime.is_some() {
1132+
self.print_list(element, mode);
1133+
return;
1134+
}
1135+
}
1136+
_ => {}
1137+
}
1138+
}
1139+
11161140
match ty {
11171141
Type::Id(t) => self.print_tyid(*t, mode),
11181142
Type::Bool => self.push_str("bool"),

crates/rust/tests/codegen.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,58 @@ mod strings {
8282
}
8383
}
8484

85+
/// Like `strings` but with a type alias.
86+
mod aliased_strings {
87+
wit_bindgen::generate!({
88+
inline: "
89+
package my:strings;
90+
91+
world not-used-name {
92+
import cat: interface {
93+
type my-string = string;
94+
foo: func(x: my-string);
95+
bar: func() -> my-string;
96+
}
97+
}
98+
",
99+
});
100+
101+
#[allow(dead_code)]
102+
fn test() {
103+
// Test the argument is `&str`.
104+
cat::foo("hello");
105+
106+
// Test the return type is `String`.
107+
let _t: String = cat::bar();
108+
}
109+
}
110+
111+
/// Like `aliased_string` but with lists instead of strings.
112+
mod aliased_lists {
113+
wit_bindgen::generate!({
114+
inline: "
115+
package my:strings;
116+
117+
world not-used-name {
118+
import cat: interface {
119+
type my-list = list<u8>;
120+
foo: func(x: my-list);
121+
bar: func() -> my-list;
122+
}
123+
}
124+
",
125+
});
126+
127+
#[allow(dead_code)]
128+
fn test() {
129+
// Test the argument is `&[u8]`.
130+
cat::foo(b"hello");
131+
132+
// Test the return type is `Vec<u8>`.
133+
let _t: Vec<u8> = cat::bar();
134+
}
135+
}
136+
85137
mod run_ctors_once_workaround {
86138
wit_bindgen::generate!({
87139
inline: "
@@ -96,7 +148,7 @@ mod run_ctors_once_workaround {
96148
});
97149
}
98150

99-
/// Like `strings` but with raw_strings`.
151+
/// Like `strings` but with `raw_strings`.
100152
mod raw_strings {
101153
wit_bindgen::generate!({
102154
inline: "

0 commit comments

Comments
 (0)