Skip to content

Commit

Permalink
[Rust] Look through typedefs to pass borrowed strings as &str (#1102)
Browse files Browse the repository at this point in the history
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>;
```
  • Loading branch information
sunfishcode authored Dec 6, 2024
1 parent d1387cc commit f3a3ac0
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 1 deletion.
24 changes: 24 additions & 0 deletions crates/rust/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,30 @@ macro_rules! {macro_name} {{
}

fn print_ty(&mut self, ty: &Type, mode: TypeMode) {
// If we have a typedef of a string or a list, the typedef is an alias
// for `String` or `Vec<T>`. If this is a borrow, instead of borrowing
// them as `&String` or `&Vec<T>`, use `&str` or `&[T]` so that callers
// don't need to create owned copies.
if let Type::Id(id) = ty {
let id = dealias(self.resolve, *id);
let typedef = &self.resolve.types[id];
match &typedef.kind {
TypeDefKind::Type(Type::String) => {
if let Some(lt) = mode.lifetime {
self.print_borrowed_str(lt);
return;
}
}
TypeDefKind::List(element) => {
if mode.lifetime.is_some() {
self.print_list(element, mode);
return;
}
}
_ => {}
}
}

match ty {
Type::Id(t) => self.print_tyid(*t, mode),
Type::Bool => self.push_str("bool"),
Expand Down
54 changes: 53 additions & 1 deletion crates/rust/tests/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,58 @@ mod strings {
}
}

/// Like `strings` but with a type alias.
mod aliased_strings {
wit_bindgen::generate!({
inline: "
package my:strings;
world not-used-name {
import cat: interface {
type my-string = string;
foo: func(x: my-string);
bar: func() -> my-string;
}
}
",
});

#[allow(dead_code)]
fn test() {
// Test the argument is `&str`.
cat::foo("hello");

// Test the return type is `String`.
let _t: String = cat::bar();
}
}

/// Like `aliased_string` but with lists instead of strings.
mod aliased_lists {
wit_bindgen::generate!({
inline: "
package my:strings;
world not-used-name {
import cat: interface {
type my-list = list<u8>;
foo: func(x: my-list);
bar: func() -> my-list;
}
}
",
});

#[allow(dead_code)]
fn test() {
// Test the argument is `&[u8]`.
cat::foo(b"hello");

// Test the return type is `Vec<u8>`.
let _t: Vec<u8> = cat::bar();
}
}

mod run_ctors_once_workaround {
wit_bindgen::generate!({
inline: "
Expand All @@ -96,7 +148,7 @@ mod run_ctors_once_workaround {
});
}

/// Like `strings` but with raw_strings`.
/// Like `strings` but with `raw_strings`.
mod raw_strings {
wit_bindgen::generate!({
inline: "
Expand Down

0 comments on commit f3a3ac0

Please sign in to comment.