Can you put struct impl blocks in a different file? - struct

Say, for purposes of conjecture, that you have three files: main.rs, struct.rs, and impl.rs. Could you define a struct in struct.rs, put an impl there, put another impl in impl.rs, and then use both sets of impls from main.rs? If so, how?
Project structure:
main.rs:
use struct;
use impl;
main() {
let foobar = struct::Struct::new(); // defined in struct.rs
foobar.x(); // defined in impl.rs
}
struct.rs:
Define Struct, first impl
impl.rs:
Second impl

Yes, this is possible. You can provide implementations for your struct throughout the crate. You just can't provide impls for types from foreign crates. And you don't need to do anything special to make this work – just make sure the struct is visible in main. Of course you can't name your modules struct and impl, since these are reserved words.
Here's some example code:
fn main() {
use struct_::A;
A::foo();
A::bar();
}
pub mod struct_ {
pub struct A;
impl A {
pub fn foo() {}
}
}
mod impl_ {
impl crate::struct_::A {
pub fn bar() {}
}
}
(Playground)

Related

Retrieve constants from inner in Newtype pattern

Is there a way to retrieve public constants from the inner struct in the Newtype pattern?
Say I am using a struct from another crate like this
#[derive(PartialEq)]
pub struct Version(u32);
impl Version {
pub const v0: Self = Self(0);
pub const v1: Self = Self(1);
}
Now, in my code I need to wrap it with a newtype pattern. So far so good.
#[derive(PartialEq)]
pub struct MyVersion(Version);
I want to get the inner constant using the wrapper type along the lines of MyVersion::v0. Is this doable?
Rust Playground link
Thanks to #Sven for suggesting a solution that works with stable Rust.
A way out of this is to implement a trait with an associated type. The end usage is awkward (due to E0223), but at least re-defining constants and associated methods is unnecessary.
// original crate
#[derive(Debug, PartialEq)]
pub struct Version(u32);
impl Version {
pub const V0: Self = Self(0);
pub const V1: Self = Self(1);
}
// another crate
use std::ops::Deref;
#[derive(Debug, PartialEq)]
pub struct MyVersion(Version);
impl Deref for MyVersion {
type Target = Version;
fn deref(&self) -> &Self::Target {
&self.0
}
}
trait Versioning {
type V;
}
impl Versioning for MyVersion {
type V = Version;
}
fn main() {
let myv0 = MyVersion(Version::V0);
assert_eq!(*myv0, <MyVersion as Versioning>::V::V0);
}
The custom trait will probably not be needed when inherent associated type lands in the stable channel.

How to have a public trait with a pub(crate) method in a library?

Suppose I have the following trait in a library:
pub trait Foo {
fn public_op(&self);
fn internal_op(&self);
}
This trait is then implemented for a bunch of structs in this library:
pub struct One {}
impl Foo for One {
fn public_op(&self) {}
fn internal_op(&self) {}
}
pub struct Two {}
impl Foo for Two {
fn public_op(&self) {}
fn internal_op(&self) {}
}
And there is a public function in this library which receives the trait type:
pub fn process(obj: &dyn Foo) {
obj.public_op();
obj.internal_op();
}
The problem is that, since Foo trait is public in the library, the method internal_op is also public... but in fact it should have pub(crate) visibility, because it must be used inside the library only.
As far as I know, all methods of a trait are public, so how can I redesign this problem?
You can split Foo into two traits, one public and one private.
pub trait Foo: private::Foo {
fn public_op(&self);
}
pub(crate) mod private {
pub trait Foo {
fn internal_op(&self);
}
}
Then implement them both in your library crate as follows:
pub struct One {}
impl Foo for One {
fn public_op(&self) {}
}
impl private::Foo for One {
fn internal_op(&self) {}
}
Then using from outside the library crate would look like:
fn main() {
let one = One {};
one.public_op(); // works
process(&one); // works
//one.internal_op(); // error[E0599]: no method named `internal_op`...
}
This does mean that its impossible for users of your library to implement Foo as Foo is now effectively a 'sealed trait'.
See Jack Wrenn's blog post for a discussion of this and alternative approaches.
Rust playground link

Is it possible to #[wasm_bindgen] public structs and functions defined in another crate?

I have a crate lib that contains tons of structs and impls. Then I have another one called web that will make the portability of the core lib to the web. There is also api in case I want the app to be server-side.
myproject-api
myproject-lib
myproject-web
What I don't want is to add all the wasm dependencies to lib, only in the web project and expose parts of the main library to the web. Is it possible to #[wasm_bindgen] the structs defined in the myproject-lib in myproject-web?
Not directly. The #[wasm_bindgen] attribute relies on being able to parse the struct and impls in order to generate the bindings. You would have to create wrapper types and functions for the attribute to bind to.
Say your myproject-lib looks like:
pub struct MyStruct {
pub foo: i32,
}
impl MyStruct {
pub fn bar(&self) {
// do something
}
}
The bindings would be implemented in myproject-web like:
use myproject_lib::*;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(js_name = MyStruct)]
pub struct MyStructWrapper(MyStruct);
#[wasm_bindgen(js_class = MyStruct)]
impl MyStructWrapper {
#[wasm_bindgen(getter)]
pub fn foo(&self) -> i32 {
self.0.foo
}
#[wasm_bindgen(setter)]
pub fn set_foo(&mut self, value: i32) {
self.0.foo = value;
}
pub fn bar(&self) {
self.0.bar();
}
}
As you can see, everything is done very explicitly.
encapsulate the original struct and export it with the original name using js_name and js_class
list out and implement getters and setters for public fields
add simple forwarding functions to the original struct.
I believe the more widely-used method is to add the bindings to the original library but only enabled by a feature. This avoids much duplication, is less of a headache to implement, and ensures the bindings are always in-sync.
Add a "wasm" feature that adds wasm-bindgen as an opt-in a dependency in your Cargo.toml:
[features]
wasm = ["wasm-bindgen"]
[dependencies]
wasm-bindgen = { version = "X.X.X", optional = true }
Then you can use cfg and cfg_attr to only enable the wasm_bindgen attributes when the feature is enabled:
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
#[cfg_attr(feature = "wasm", wasm_bindgen)]
pub struct MyStruct {
pub foo: i32,
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
impl MyStruct {
pub fn bar(&self) {
// do something
}
}

Simplify a long namespace for impl when importing the type would introduce ambiguity?

Is there a way to simplify namespace for the following code?
struct T {...}
impl<'a> From<A::B::C::D::Foo<'a>> for T {
fn from(x: A::B::C::D::Foo<'a>) -> Self {
T {...}
}
}
I don't want to use A::B::C::D::Foo in the current module as Foo might bring in namespace ambiguity.
Is there a way to temporarily/locally use A::B::C::D::Foo for the impl? It seems I can only do that within a function scope, rather than an impl scope.
Currently, my workaround is to use a dummy module.
struct T {...}
mod abc {
use super::T;
use A::B::C::D::Foo;
impl<'a> From<Foo<'a>> for T {
fn from(x: Foo<'a>) -> Self {
T {...}
}
}
}
If using a nested module is the canonical solution, is it possible to define an anonymous module since the module name is unimportant?
Note, I don't mind writing A::B::C::D::Foo once for impl, but since the type in from is the same as the one in impl From, I am looking for ways to NOT have to write the same A::B::C::D::Foo twice.
as Foo might bring in namespace ambiguity
You can rename the type when you import it to avoid ambiguity:
pub mod a {
pub mod b {
pub mod c {
pub mod d {
pub struct Foo;
}
}
}
}
struct T;
use a::b::c::d::Foo as UniqueName;
impl<'a> From<UniqueName> for T {
fn from(_: UniqueName) -> Self {
T
}
}
fn main() {}
You could also use a type alias:
type UniqueName = a::b::c::d::Foo;

Source trait is inaccessible

The situation is (severely simplified) this (playpen):
mod tokentree {
pub struct TokenTree;
mod serialize {
use std::collections::BTreeMap;
use super::TokenTree;
#[derive(Debug)]
pub struct InternStr;
pub trait InternStrsExt {
fn intern_strs(&self) -> BTreeMap<&str, InternStr>;
}
impl InternStrsExt for [TokenTree] {
fn intern_strs(&self) -> BTreeMap<&str, InternStr> { BTreeMap::new() }
}
}
pub use self::serialize::{InternStrsExt, InternStr};
}
use tokentree::*;
fn main() {
println!("{:?}", [TokenTree].intern_strs());
}
I get the following error (both on nightly and beta):
<anon>:20:22: 20:47 error: source trait is inaccessible
<anon>:20 println!("{:?}", [TokenTree].intern_strs());
^~~~~~~~~~~~~~~~~~~~~~~~~
My problem is that I don't even know what this is supposed to mean.
It needs a pub declaration. Also your declarations are all over the place. Recommended form is to stick your pub mod declarations first, then, use.
Here is the working example.
mod tokentree {
pub struct TokenTree;
pub mod serialize {
use std::collections::BTreeMap;
use super::TokenTree;
#[derive(Debug)]
pub struct InternStr;
pub trait InternStrsExt {
fn intern_strs(&self) -> BTreeMap<&str, InternStr>;
}
impl InternStrsExt for [TokenTree] {
fn intern_strs(&self) -> BTreeMap<&str, InternStr> { BTreeMap::new() }
}
}
pub use self::serialize::{InternStrsExt, InternStr};
}
pub use tokentree::*;
fn main() {
println!("{:?}", [TokenTree].intern_strs());
}
(playpen)
What happened here is that you stumbled upon following glitches:
https://github.com/rust-lang/rust/issues/18241
https://github.com/rust-lang/rust/issues/16264
You can't export your traits from a private module. That's why you need to change mod serialize into pub mod serialize. For example this playpen example demonstrates that exporting struct Export works, but un-commenting the println! will make it stop compiling, because we used a trait.
Tip: One thing that helps me with the visibility rules is to generate doc files and see which doc files are visible.

Resources