Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infer function signatures from trait declaration into 'impl's #2063

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
766e6a2
added relative use mod import rfc
Jul 1, 2014
d805fd0
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 1, 2014
1c9287f
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 1, 2014
6253320
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 1, 2014
46ca288
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 1, 2014
0c18002
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 1, 2014
8b38f7a
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 1, 2014
439b3bf
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 1, 2014
7088105
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 2, 2014
76ce70a
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 2, 2014
0e1e8ea
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 2, 2014
839ce69
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 2, 2014
fad91bc
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 2, 2014
c8a1a18
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 2, 2014
ebc35a9
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 2, 2014
ea49d50
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 2, 2014
aef7749
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 2, 2014
d32161d
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 8, 2014
155437b
Update 0000-relative-use-mod-imports.md
dobkeratops Jul 12, 2014
e4ebfa7
Update and rename 0000-template.md to text/0000-ask_the_compiler_plac…
dobkeratops Jul 3, 2017
9ca7feb
Update 0000-ask_the_compiler_placeholder_ident.md
dobkeratops Jul 3, 2017
cba3f5d
Update 0000-ask_the_compiler_placeholder_ident.md
dobkeratops Jul 3, 2017
82f967a
Update 0000-ask_the_compiler_placeholder_ident.md
dobkeratops Jul 13, 2017
2299860
Update 0000-ask_the_compiler_placeholder_ident.md
dobkeratops Jul 13, 2017
5fdffaa
Update 0000-ask_the_compiler_placeholder_ident.md
dobkeratops Jul 13, 2017
5233085
Update 0000-ask_the_compiler_placeholder_ident.md
dobkeratops Jul 13, 2017
47b67a0
Rename 0000-ask_the_compiler_placeholder_ident.md to infer function p…
dobkeratops Jul 13, 2017
184dbd4
Rename infer function parameter and return types from the original tr…
dobkeratops Jul 13, 2017
a5090a6
Update infer function signatures from trait declaration into impls
dobkeratops Jul 13, 2017
4ee9e16
Update infer function signatures from trait declaration into impls
dobkeratops Jul 14, 2017
29f7020
Update infer function signatures from trait declaration into impls
dobkeratops Jul 14, 2017
cafd5c5
Update infer function signatures from trait declaration into impls
dobkeratops Jul 14, 2017
22d2f78
Rename infer function signatures from trait declaration into impls to…
dobkeratops Jul 22, 2017
c6846a0
Update infer function signatures from trait declaration into impls.md
dobkeratops Jul 22, 2017
438be12
Update infer function signatures from trait declaration into impls.md
dobkeratops Jul 22, 2017
c78d53e
Update infer function signatures from trait declaration into impls.md
dobkeratops Jul 29, 2017
e5a7403
Update infer function signatures from trait declaration into impls.md
dobkeratops Jul 29, 2017
def4117
Update infer function signatures from trait declaration into impls.md
dobkeratops Sep 6, 2017
8eeff46
Update infer function signatures from trait declaration into impls.md
dobkeratops Sep 6, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 0 additions & 29 deletions 0000-template.md

This file was deleted.

101 changes: 101 additions & 0 deletions active/0000-relative-use-mod-imports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
- Start Date: 2014-06-01
- RFC PR #: (leave this empty)
- Rust Issue #: (leave this empty)

# Summary

introduce ```use mod ...;``` (or ```import ...;``` ) as a simultaneous import and 'use', with relative module paths, which have a 1:1 mapping to relative filename paths.

``` use mod foo::bar::baz``` brings in module foo/bar/baz.rs & uses' bar.

Creates a graph of imports between files as in other module systems, but still mounts modules heriarchically, mirroring the directory tree.

```use mod``` brings a module into scope along with the hint: "this module is an actual file"




# Motivation

## avoids repeating information

This idea exploits coherence between the module heirarchy and the filesystem directory tree - but it *assumes* this coherence, instead of relying on the user to manually *create* it with 'mod.rs' files. So the information you give when 'bringing things into scope' should be enough to specify what to load.


## versatility for compile units

Consider moving between the extremes of one compilation unit per file, vs an entire project as a single compilation unit - with the existing use/mod behaviour, you would have to refactor how modules are brought in and how components are referenced.

a faster debug build with less inlining might be possible with smaller translation units; or you want to switch to a single translation unit (like C++ unity builds) for the maximum optimization possible.

Relative paths would allow greater flexibility when wanting to treat project subtrees as seperate libraries, or vica versa. eg. for building examples demonstrating components of an SDK, or a single source tree building a suite of tools.

A build system would be at liberty to cache any appropriate subtree equivalently to a library crate with no pre-planning on the users' part.


## shorter learning curve
The seperate absolute and relative paths, and mod / use statements are a tripping point for new users. Under this scheme, you only see relative paths, and you only need one statement 'use mod '.

eliminates the need to create seperate ```mod.rs``` files within directories. Each file would do the job of mod.rs specifying further files to bring in.

## parallelize --test
This might be useful for compiling tests, eg it would be theoretically possible to start at any individual file and test it, in isolation from the whole-project build. So building for test could be done across more cores.

## tooling
with a project setup this way, a tool can locate definitions starting at any 'current' file and spidering outward. While working on a project, one may have source from different component libraries open; Under the current system, each of which would have different addressing scheme, relative to its own crate root. Under this scheme, a tools needs to know less about the whole project to give consistent help to the user.

# Detailed design

```use mod``` would look for a file relative to the current file.

given some source files:

foo.rs
bar.rs
baz/qux.rs
../qaz.rs

from ```foo.rs,``` the following statements

use mod bar;
use mod baz::qux;
use mod super::qaz;

would add ```foo.rs, bar.rs, baz/qux.rs, ../qaz.rs ``` to the project (eg, baz::qux is like saying 'load baz/qux.rs'), and make ```bar::,qux::,qaz::``` available as qualifiers within foo.rs .

This would work regardless whether ```foo.rs``` was the crate root or further down the tree.

Further ```use``` statements could give shortcuts to individual symbols, and longer paths could be written to access subtrees of these modules.

Each individual file would in turn be able to bring in its own relative files - starting from the project root, the build system would spider outward.

eg if qux.rs contained the statement ```use mod super::super::qaz;``` , ```../qaz.rs``` would be brought into the project, although 'foo.rs' would still need an additional ```use super::qaz``` to reference symbols in ```qaz.rs```.

## use mod between siblings
Symbol paths would always reflect the directory-structure: - when a series of siblings reference eachother, one would not be able to follow this graph to reach symbols. eg if there is a relationship a.rs->b.rs->c.rs but they are all in the same directory, there is no path ```a::b::c```, just seperate ```a:: b:: c::```

##submodules wthin files
mod {...} within a file would still be available - this is where the module heirarchy can differ from the file layout, but its assumed every file must be referenced explicitely by a ```use mod``` statement. (submodules would be reached with additional ```use```'s

## use vs use mod
if it wasn't for the existence of submodules, would it be possible to infer load information entirely from relative use directives, and individual qualified symbols ? However this system relies on "use mod" as a hint, "this module is a file"

# Drawbacks

The behaviour of the standard library prelude might not seem as consistent with this scheme.

Replicates functionality available with use, mod and #[path=...] directives, and is a slightly different mentality to the existing system.

Might look more complicated *when used alonside the existing system* (even though its' intended as a replacement, it would require a rolling refactor)

heirachical 'use' paths have their own problems. When moving sources up or down the directory tree, refactoring would still be needed; Rust supposedly already moved from relative to absolute.

If this was to replace the existing use/mod behaviour, one might need references to a long string of ```use mod super::super::super::...::main``` statements to refer to symbols relative to the project root.

perhaps the tree flattening effect of explicit crate files which are them imported into a project root is desirable.
(under this scheme, *every* source file that wants to refer to a particular crate conveiniently would have some ```use mod super::super..some_major_module_that_would_currently_be_a_crate```)

if modules down the graph did import files earlier in the tree, the tool would have to warn you about this and possibly dissalow when you compile a subtree as a library crate.



113 changes: 113 additions & 0 deletions text/infer function signatures from trait declaration into impls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
- Start Date: (fill me in with today's date, YYYY-MM-DD)
- RFC PR #: (leave this empty)
- Rust Issue #: (leave this empty)

# Summary

Allow the ommision of function parameters and return types, in the implementation of a trait for a type;
Infer this information directly from the trait declaration.

# Motivation

Rust signatures can become quite heavy with nested angle-bracketed types, trait-bounds, lifetimes etc,
Also,coming from C++, the need to write (and reference) traits *before* you can write 'overloads' comes as a shock,
especially for people trying to escape the repitition of 'header files'.

(Furthermore, the types are in a different location ```type argname``` vs ```argname:type``` .. although more logical there is a cost to any reading convention-switch )

However, if the trait was used to *avoid* repeating information,
they would come across more obviously as a virtue:
You would leverage the trait name to specify many detailed signatures.

Note that this would not make writing the implementation any harder:-

Unlike with general purpose whole-program inference , constraining is already implied (unambiguously) by the trait itself;
The compiler already knows that one must match the other, and when it doesn't it reports an error.

Compared to C++, Rusts syntax allows the ommision of types whilst still parsing parameter names in a straightforward manner,
creating this opportunity.
The lack of overloading *within* a trait/impl means there is no need to write the types to disambiguate the functions;
you would be fully leveraging the trait name to do that.

Behaviour of this type can be seen in the Haskell language, i.e:-

class FooBar f where -- typeclass definition (roughly = Rust trait)
foo::f->i32->[i32]->String -- only write function signatures
bar::f->[String]->String->Maybe String

instance FooBar Apple where -- typeclass instance (roughly = Rust impl)
foo s x y = /*..1..*/ -- only write the argument names and function definition
bar s z w = /*..2..*/

instance FooBar Banana where
foo s x y = /*..3..*/ -- only write the argument names and function definition
bar s z w = /*..4..*/


(/*..1..*/ etc denote function definition bodies)

Surprisingly, coming from C++ (where familiar users read ```Type argname```), a seperation into 'just types', and 'just names' is actually easier to adapt to (the absence of a detail, rather than the detail being in a different place)


The trait is still usually very easy to find - thanks to Rusts syntax, declarations are easy to grep for.

# Detailed design

by example: the proposal is to allow the following to compile
(```/*..1..*/``` etc denote the function definition bodies roughly equivalent to the pattern above)

struct Apple(i32); struct Banana(String)
trait FooBar {
fn foo(&self, x:i32, y:Vec<i32>)->i32;
fn bar(&self, z:&Vec<String>, w:&String)->Option<String>;
}

impl FooBar for Apple {
fn foo(&self, x,y){ /*..1..*/ } // no need to repeat :i32 :Vec<i32> ->i32
fn bar(&self, z,w){ /*..2..*/ } // no need to repeat :&Vec<String> , :String -> Option<String>
}

impl FooBar for Banana {
fn foo(&self, x,y){ /*..3..*/ } // no need to repeat :i32 ->i32
fn bar(&self, z,w){ /*..4..*/ } // no need to repeat :Vec<String> , :String -> Option<String>
}

Trait definitions are easy to grep, but to further streamline locating them, a "File:line: -see definition of <trait Foo>" styl error message would tend to bring these into the text-editor as the user steps through error messages in the usual edit-compile cycle.

## concern about return values

A variation would be to require a trailing placeholder ```->_``` to make it clearer if there is a return value


# Drawbacks


One potential objection is that you can no longer see the types when you read the impl.

However, whilst engaged in the compile-edit cycle, the compiler can directly report what the types should be,
if you make an error;
also the programmer *must* have the trait documentation or original source at hand
(or gain enough guidance from the error message) in order to actually write the implementation in the first place.

as far as users go, refering to the trait should suffice; there may be example code close to the trait decl;
trait implemenations are easy to search for, thanks to rusts syntax. *RustDoc* already gives browseable reference; trait declarations are very easy to grep for, and future IDE tools may have other interactive assists.

As such , this should not be a problem - even for cross-module implementations


# Alternatives

* allowing more general whole-program inference;
* another way to streamline the number of mental steps when writing trait 'impls'
would be to swap the trait/self-type order as this is more coherent with the function declarations themselves
(no need to mentally swap them back and forth), as well as 'trait object casting' (type as trait)
and disambiguation syntax <X as Trait>::method_name() ;
this would be the subject of another RFC (a very different request aimed at an overlapping scenario)
It would be an alternate declaration syntax complemeting the existing one.
impl type as trait {..}, or impl type:trait {} It would complement this suggestion.

* Getting back to the expectations from C++, if writing the types out, one might expect the reverse: that the language could infer which *trait* is being implemented, allowing that to be ommitted; unfortunately this goes against the idea that the trait is part of the function's namespace.

# Unresolved questions

What parts of the design are still TBD?