Why does calling a method on a variable prevent Rust from inferring the type of the variable?How does Rust's type inference work across multiple statements?How does Rust infer resultant types from From::<>::from()?Inferred type appears to detect an infinite loop, but what's really happening?What does VB.Net For Each Loop look at to Infer the TypeHow does Typed Racket's type inference work?Why can't the compiler infer the type for this select call?Default generic type parameter cannot be inferredWhat algorithms does the Rust compiler use to infer lifetime variables?Why do I get the error “expected type argument” when I annotate a type in a Rust function?How does Rust solve mutability for Hindley-Milner?Why can't Rust do more complex type inference?Does F# type inference works top-down (and left-right) only?

What are the words for people who cause trouble believing they know better?

Did Darth Vader wear the same suit for 20+ years?

The ring of global sections of a regular scheme

Secure offsite backup, even in the case of hacker root access

How were concentration and extermination camp guards recruited?

Chopin: marche funèbre bar 15 impossible place

Personalization conditions switching doesn`t work in Experience Editor (9.1.0, Initial Release)

Humans meet a distant alien species. How do they standardize? - Units of Measure

The term for the person/group a political party aligns themselves with to appear concerned about the general public

Do I include animal companions when calculating difficulty of an encounter?

Why don't B747s start takeoffs with full throttle?

Convert camelCase and PascalCase to Title Case

When writing an error prompt, should we end the sentence with a exclamation mark or a dot?

What does War Machine's "Canopy! Canopy!" line mean in "Avengers: Endgame"?

What happens to foam insulation board after you pour concrete slab?

Opposite of "Squeaky wheel gets the grease"

Bent spoke design wheels — feasible?

What does it mean by "d-ism of Leibniz" and "dotage of Newton" in simple English?

Incremental Ranges!

What happened to all the nuclear material being smuggled after the fall of the USSR?

Is it legal in the UK for politicians to lie to the public for political gain?

Is it a problem that pull requests are approved without any comments

Is the capacitor drawn or wired wrongly?

Pros and cons of writing a book review?



Why does calling a method on a variable prevent Rust from inferring the type of the variable?


How does Rust's type inference work across multiple statements?How does Rust infer resultant types from From::<>::from()?Inferred type appears to detect an infinite loop, but what's really happening?What does VB.Net For Each Loop look at to Infer the TypeHow does Typed Racket's type inference work?Why can't the compiler infer the type for this select call?Default generic type parameter cannot be inferredWhat algorithms does the Rust compiler use to infer lifetime variables?Why do I get the error “expected type argument” when I annotate a type in a Rust function?How does Rust solve mutability for Hindley-Milner?Why can't Rust do more complex type inference?Does F# type inference works top-down (and left-right) only?






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;








30















This code compiles:



#[derive(Debug, Default)]
struct Example;

impl Example
fn some_method(&self)


fn reproduction() -> Example
let example = Default::default();
// example.some_method();
example



If the commented line is added back, it will cause an error:



error[E0282]: type annotations needed
--> src/lib.rs:10:5
|
9 | let example = Default::default();
| ------- consider giving `example` a type
10 | example.some_method();
| ^^^^^^^ cannot infer type
|
= note: type must be known at this point


Why does adding this method call cause type inference to fail?



I've seen these two questions:



  • How does Rust's type inference work across multiple statements?

  • How does Rust infer resultant types from From::<>::from()?

From them, I know that Rust uses a (modified) version of Hindley-Milner. The latter question has an answer that describes Rust's type inference as a system of equations. Another answer explicitly states that "Type information in Rust can flow backwards".



Using this knowledge applied to this situation, we have:




  1. example is type ?E


  2. ?E must have a method called some_method


  3. ?E is returned

  4. The return type is Example

Working backward, it's easy for a human to see that ?E must be Example. Where is the gap between what I can see and what the compiler can see?










share|improve this question



















  • 3





    Note that this question is inspired from another question. The author of that question did not appear to want to provide a MCVE, so I've created this question as a cleaner example.

    – Shepmaster
    Mar 19 at 14:45






  • 1





    modified? - yes, see Type Inference: The type inference is based on the standard Hindley-Milner (HM) type inference algorithm, but extended in various way to accommodate subtyping, region inference, and higher-ranked types.

    – zrzka
    Mar 19 at 15:22











  • I wonder if this is something that chalk can help with when it lands…

    – Jmb
    Mar 19 at 15:30











  • Tried with -Z chalk (nightly) and same result.

    – zrzka
    Mar 19 at 20:11

















30















This code compiles:



#[derive(Debug, Default)]
struct Example;

impl Example
fn some_method(&self)


fn reproduction() -> Example
let example = Default::default();
// example.some_method();
example



If the commented line is added back, it will cause an error:



error[E0282]: type annotations needed
--> src/lib.rs:10:5
|
9 | let example = Default::default();
| ------- consider giving `example` a type
10 | example.some_method();
| ^^^^^^^ cannot infer type
|
= note: type must be known at this point


Why does adding this method call cause type inference to fail?



I've seen these two questions:



  • How does Rust's type inference work across multiple statements?

  • How does Rust infer resultant types from From::<>::from()?

From them, I know that Rust uses a (modified) version of Hindley-Milner. The latter question has an answer that describes Rust's type inference as a system of equations. Another answer explicitly states that "Type information in Rust can flow backwards".



Using this knowledge applied to this situation, we have:




  1. example is type ?E


  2. ?E must have a method called some_method


  3. ?E is returned

  4. The return type is Example

Working backward, it's easy for a human to see that ?E must be Example. Where is the gap between what I can see and what the compiler can see?










share|improve this question



















  • 3





    Note that this question is inspired from another question. The author of that question did not appear to want to provide a MCVE, so I've created this question as a cleaner example.

    – Shepmaster
    Mar 19 at 14:45






  • 1





    modified? - yes, see Type Inference: The type inference is based on the standard Hindley-Milner (HM) type inference algorithm, but extended in various way to accommodate subtyping, region inference, and higher-ranked types.

    – zrzka
    Mar 19 at 15:22











  • I wonder if this is something that chalk can help with when it lands…

    – Jmb
    Mar 19 at 15:30











  • Tried with -Z chalk (nightly) and same result.

    – zrzka
    Mar 19 at 20:11













30












30








30


3






This code compiles:



#[derive(Debug, Default)]
struct Example;

impl Example
fn some_method(&self)


fn reproduction() -> Example
let example = Default::default();
// example.some_method();
example



If the commented line is added back, it will cause an error:



error[E0282]: type annotations needed
--> src/lib.rs:10:5
|
9 | let example = Default::default();
| ------- consider giving `example` a type
10 | example.some_method();
| ^^^^^^^ cannot infer type
|
= note: type must be known at this point


Why does adding this method call cause type inference to fail?



I've seen these two questions:



  • How does Rust's type inference work across multiple statements?

  • How does Rust infer resultant types from From::<>::from()?

From them, I know that Rust uses a (modified) version of Hindley-Milner. The latter question has an answer that describes Rust's type inference as a system of equations. Another answer explicitly states that "Type information in Rust can flow backwards".



Using this knowledge applied to this situation, we have:




  1. example is type ?E


  2. ?E must have a method called some_method


  3. ?E is returned

  4. The return type is Example

Working backward, it's easy for a human to see that ?E must be Example. Where is the gap between what I can see and what the compiler can see?










share|improve this question
















This code compiles:



#[derive(Debug, Default)]
struct Example;

impl Example
fn some_method(&self)


fn reproduction() -> Example
let example = Default::default();
// example.some_method();
example



If the commented line is added back, it will cause an error:



error[E0282]: type annotations needed
--> src/lib.rs:10:5
|
9 | let example = Default::default();
| ------- consider giving `example` a type
10 | example.some_method();
| ^^^^^^^ cannot infer type
|
= note: type must be known at this point


Why does adding this method call cause type inference to fail?



I've seen these two questions:



  • How does Rust's type inference work across multiple statements?

  • How does Rust infer resultant types from From::<>::from()?

From them, I know that Rust uses a (modified) version of Hindley-Milner. The latter question has an answer that describes Rust's type inference as a system of equations. Another answer explicitly states that "Type information in Rust can flow backwards".



Using this knowledge applied to this situation, we have:




  1. example is type ?E


  2. ?E must have a method called some_method


  3. ?E is returned

  4. The return type is Example

Working backward, it's easy for a human to see that ?E must be Example. Where is the gap between what I can see and what the compiler can see?







rust type-inference type-systems hindley-milner






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Mar 19 at 15:24







Shepmaster

















asked Mar 19 at 14:43









ShepmasterShepmaster

167k17360510




167k17360510







  • 3





    Note that this question is inspired from another question. The author of that question did not appear to want to provide a MCVE, so I've created this question as a cleaner example.

    – Shepmaster
    Mar 19 at 14:45






  • 1





    modified? - yes, see Type Inference: The type inference is based on the standard Hindley-Milner (HM) type inference algorithm, but extended in various way to accommodate subtyping, region inference, and higher-ranked types.

    – zrzka
    Mar 19 at 15:22











  • I wonder if this is something that chalk can help with when it lands…

    – Jmb
    Mar 19 at 15:30











  • Tried with -Z chalk (nightly) and same result.

    – zrzka
    Mar 19 at 20:11












  • 3





    Note that this question is inspired from another question. The author of that question did not appear to want to provide a MCVE, so I've created this question as a cleaner example.

    – Shepmaster
    Mar 19 at 14:45






  • 1





    modified? - yes, see Type Inference: The type inference is based on the standard Hindley-Milner (HM) type inference algorithm, but extended in various way to accommodate subtyping, region inference, and higher-ranked types.

    – zrzka
    Mar 19 at 15:22











  • I wonder if this is something that chalk can help with when it lands…

    – Jmb
    Mar 19 at 15:30











  • Tried with -Z chalk (nightly) and same result.

    – zrzka
    Mar 19 at 20:11







3




3





Note that this question is inspired from another question. The author of that question did not appear to want to provide a MCVE, so I've created this question as a cleaner example.

– Shepmaster
Mar 19 at 14:45





Note that this question is inspired from another question. The author of that question did not appear to want to provide a MCVE, so I've created this question as a cleaner example.

– Shepmaster
Mar 19 at 14:45




1




1





modified? - yes, see Type Inference: The type inference is based on the standard Hindley-Milner (HM) type inference algorithm, but extended in various way to accommodate subtyping, region inference, and higher-ranked types.

– zrzka
Mar 19 at 15:22





modified? - yes, see Type Inference: The type inference is based on the standard Hindley-Milner (HM) type inference algorithm, but extended in various way to accommodate subtyping, region inference, and higher-ranked types.

– zrzka
Mar 19 at 15:22













I wonder if this is something that chalk can help with when it lands…

– Jmb
Mar 19 at 15:30





I wonder if this is something that chalk can help with when it lands…

– Jmb
Mar 19 at 15:30













Tried with -Z chalk (nightly) and same result.

– zrzka
Mar 19 at 20:11





Tried with -Z chalk (nightly) and same result.

– zrzka
Mar 19 at 20:11












2 Answers
2






active

oldest

votes


















20





+100









Based on known facts (see below), it fails to compile because:



  • the type checker goes through the function in the order it was written,

  • in let example = Default::default();, example can be anything which implements Default,

  • field accesses & method calls require a known type,

  • "anything implementing Default" is not a known type.

I replaced some_method() with a field access and it produces same error.




From Type inference depends on ordering (#42333):




use std::path::PathBuf;

pub struct Thing
pub f1: PathBuf,


fn junk() -> Vec<Thing>
let mut things = Vec::new();
for x in vec![1, 2, 3]
if x == 2
for thing in things.drain(..)
thing.f1.clone();

return vec![]

things.push(Thingf1: PathBuf::from(format!("/", x)));

things


fn main()
junk();




This produces a compiler error with Rust 1.33.0:



error[E0282]: type annotations needed
--> src/main.rs:13:17
|
9 | let mut things = Vec::new();
| ---------- consider giving `things` a type
...
13 | thing.f1.clone();
| ^^^^^ cannot infer type
|
= note: type must be known at this point


You should focus on the following comments from eddyb (a well-known member of the the Rust language design team since May, 2016).



Comment #1:




This is a known limitation of the in-order type-checker. While inference flows freely, thing.f1.clone() is checked before things.push(Thing ...) so it isn't known that thing: Thing when you try to access the f1 field. We may in the future move away from this, but there are no immediate plans.




What's more important is comment #2:




What I mean is that the type-checker goes through the function in the order it was written. [...] Fields accesses and methods calls are simply not supported unless the type is already known.







share|improve this answer




















  • 2





    I don't think we need proof that eddyb is a prominent member; just a note next his name when you give the first comments would be enough. I think this answer would be improved by removing the Proof and Temporary sections; you can comment on your own answers for work in progress stuff, etc... no need to scare people with a longer answer when 1/3 doesn't actually answer. // // // On another note: awesome digging!

    – Matthieu M.
    Mar 20 at 16:26






  • 1





    1) No official documentation for the in-order type checking (incl. Rust compiler docs) 2) Lot of other issues / PRs mention in-order with a note that it's a known limitation 3) It's covered by tests like this one 4) Recompiled rustc with debug assertions and matched the output with the source code. I can confirm that there's in-order type checking (but not the rustc expert, still may be wrong). Impossible to summarize my findings in a short answer (too long, complicated, so many paths).

    – zrzka
    Mar 26 at 10:19






  • 1





    Based on summary from my previous comment, I'm affraid that there's not much else to add. I'm happy with what I've found, learned some internals, but I also agree that this isn't satisfying / very good answer. It's probably all we can do now unless some rust compiler team member steps in. If you want to play with it, here're some notes from Niko describing the fastest way to get a working compiler (if you'd like to rebuild with debug assertions).

    – zrzka
    Mar 26 at 10:29






  • 2





    I personally find your answer very satisfying; sure it's not a "proof", but I don't think the Rust language is willing to enshrine type inference in the specification (as it leads to stagnation), so I suspect that studying the code and referencing known limitations is the best we can get.

    – Matthieu M.
    Mar 26 at 12:38






  • 1





    Happy 10K reputation!

    – Shepmaster
    Mar 31 at 14:13


















8














I don't know the full answer and I have next to no knowledge of the internal workings of the Rust compiler, but here are some deductions I've come to from my experience with Rust.



Information about types in Rust can "flow backwards", but there are certain times when Rust needs to know (for absolute certain) the type of an expression. In these situations, it must "already" know the type, i.e. it will not continue to look forward.



From what I've seen, this situation is limited to method calls. I suspect it has something to do with the fact that methods can be implemented on traits, which substantially complicates things. I doubt that there are any traits in scope with a method named some_method, but I think that whenever the Rust compiler encounters a method call it requires the type to already be known for certain.



You can see this happen a lot with method calls on types which implement traits, the most common being the collect method on a type that implements the Iter trait. You will be able to call collect, but won't be able to call any methods on the result unless you specify the type.



So this works:



fn create_numbers(last_num: i32) -> Vec<i32> 
let x = (0..10).collect();
x



But this does not:



fn create_numbers(last_num: i32) -> Vec<i32> 
let x = (0..10).collect();
// In order to call `push`, we need to *already* know the type
// of x for "absolute certain", and the Rust compiler doesn't
// keep looking forward
x.push(42);
x






share|improve this answer























  • "I suspect it has something to do with the fact that methods can be implemented on traits" — I don't think it's specifically about the possibility of there being an ambiguous trait. I think it's just that the type must be known at the point of calling a method, in order to know which function is intended. It could be a trait but there could also be multiple types with this method in their impl.

    – Peter Hall
    Mar 19 at 16:29











  • I appreciate the response, but isn't a very satisfying answer. The second half of the post (starting at "You can see...") is simply a restatement of the problem in the question and doesn't provide an answer. The first half correlates with my experiences, but, as you point out, isn't based on any particular authoritative source.

    – Shepmaster
    Mar 19 at 16:41












Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55243660%2fwhy-does-calling-a-method-on-a-variable-prevent-rust-from-inferring-the-type-of%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









20





+100









Based on known facts (see below), it fails to compile because:



  • the type checker goes through the function in the order it was written,

  • in let example = Default::default();, example can be anything which implements Default,

  • field accesses & method calls require a known type,

  • "anything implementing Default" is not a known type.

I replaced some_method() with a field access and it produces same error.




From Type inference depends on ordering (#42333):




use std::path::PathBuf;

pub struct Thing
pub f1: PathBuf,


fn junk() -> Vec<Thing>
let mut things = Vec::new();
for x in vec![1, 2, 3]
if x == 2
for thing in things.drain(..)
thing.f1.clone();

return vec![]

things.push(Thingf1: PathBuf::from(format!("/", x)));

things


fn main()
junk();




This produces a compiler error with Rust 1.33.0:



error[E0282]: type annotations needed
--> src/main.rs:13:17
|
9 | let mut things = Vec::new();
| ---------- consider giving `things` a type
...
13 | thing.f1.clone();
| ^^^^^ cannot infer type
|
= note: type must be known at this point


You should focus on the following comments from eddyb (a well-known member of the the Rust language design team since May, 2016).



Comment #1:




This is a known limitation of the in-order type-checker. While inference flows freely, thing.f1.clone() is checked before things.push(Thing ...) so it isn't known that thing: Thing when you try to access the f1 field. We may in the future move away from this, but there are no immediate plans.




What's more important is comment #2:




What I mean is that the type-checker goes through the function in the order it was written. [...] Fields accesses and methods calls are simply not supported unless the type is already known.







share|improve this answer




















  • 2





    I don't think we need proof that eddyb is a prominent member; just a note next his name when you give the first comments would be enough. I think this answer would be improved by removing the Proof and Temporary sections; you can comment on your own answers for work in progress stuff, etc... no need to scare people with a longer answer when 1/3 doesn't actually answer. // // // On another note: awesome digging!

    – Matthieu M.
    Mar 20 at 16:26






  • 1





    1) No official documentation for the in-order type checking (incl. Rust compiler docs) 2) Lot of other issues / PRs mention in-order with a note that it's a known limitation 3) It's covered by tests like this one 4) Recompiled rustc with debug assertions and matched the output with the source code. I can confirm that there's in-order type checking (but not the rustc expert, still may be wrong). Impossible to summarize my findings in a short answer (too long, complicated, so many paths).

    – zrzka
    Mar 26 at 10:19






  • 1





    Based on summary from my previous comment, I'm affraid that there's not much else to add. I'm happy with what I've found, learned some internals, but I also agree that this isn't satisfying / very good answer. It's probably all we can do now unless some rust compiler team member steps in. If you want to play with it, here're some notes from Niko describing the fastest way to get a working compiler (if you'd like to rebuild with debug assertions).

    – zrzka
    Mar 26 at 10:29






  • 2





    I personally find your answer very satisfying; sure it's not a "proof", but I don't think the Rust language is willing to enshrine type inference in the specification (as it leads to stagnation), so I suspect that studying the code and referencing known limitations is the best we can get.

    – Matthieu M.
    Mar 26 at 12:38






  • 1





    Happy 10K reputation!

    – Shepmaster
    Mar 31 at 14:13















20





+100









Based on known facts (see below), it fails to compile because:



  • the type checker goes through the function in the order it was written,

  • in let example = Default::default();, example can be anything which implements Default,

  • field accesses & method calls require a known type,

  • "anything implementing Default" is not a known type.

I replaced some_method() with a field access and it produces same error.




From Type inference depends on ordering (#42333):




use std::path::PathBuf;

pub struct Thing
pub f1: PathBuf,


fn junk() -> Vec<Thing>
let mut things = Vec::new();
for x in vec![1, 2, 3]
if x == 2
for thing in things.drain(..)
thing.f1.clone();

return vec![]

things.push(Thingf1: PathBuf::from(format!("/", x)));

things


fn main()
junk();




This produces a compiler error with Rust 1.33.0:



error[E0282]: type annotations needed
--> src/main.rs:13:17
|
9 | let mut things = Vec::new();
| ---------- consider giving `things` a type
...
13 | thing.f1.clone();
| ^^^^^ cannot infer type
|
= note: type must be known at this point


You should focus on the following comments from eddyb (a well-known member of the the Rust language design team since May, 2016).



Comment #1:




This is a known limitation of the in-order type-checker. While inference flows freely, thing.f1.clone() is checked before things.push(Thing ...) so it isn't known that thing: Thing when you try to access the f1 field. We may in the future move away from this, but there are no immediate plans.




What's more important is comment #2:




What I mean is that the type-checker goes through the function in the order it was written. [...] Fields accesses and methods calls are simply not supported unless the type is already known.







share|improve this answer




















  • 2





    I don't think we need proof that eddyb is a prominent member; just a note next his name when you give the first comments would be enough. I think this answer would be improved by removing the Proof and Temporary sections; you can comment on your own answers for work in progress stuff, etc... no need to scare people with a longer answer when 1/3 doesn't actually answer. // // // On another note: awesome digging!

    – Matthieu M.
    Mar 20 at 16:26






  • 1





    1) No official documentation for the in-order type checking (incl. Rust compiler docs) 2) Lot of other issues / PRs mention in-order with a note that it's a known limitation 3) It's covered by tests like this one 4) Recompiled rustc with debug assertions and matched the output with the source code. I can confirm that there's in-order type checking (but not the rustc expert, still may be wrong). Impossible to summarize my findings in a short answer (too long, complicated, so many paths).

    – zrzka
    Mar 26 at 10:19






  • 1





    Based on summary from my previous comment, I'm affraid that there's not much else to add. I'm happy with what I've found, learned some internals, but I also agree that this isn't satisfying / very good answer. It's probably all we can do now unless some rust compiler team member steps in. If you want to play with it, here're some notes from Niko describing the fastest way to get a working compiler (if you'd like to rebuild with debug assertions).

    – zrzka
    Mar 26 at 10:29






  • 2





    I personally find your answer very satisfying; sure it's not a "proof", but I don't think the Rust language is willing to enshrine type inference in the specification (as it leads to stagnation), so I suspect that studying the code and referencing known limitations is the best we can get.

    – Matthieu M.
    Mar 26 at 12:38






  • 1





    Happy 10K reputation!

    – Shepmaster
    Mar 31 at 14:13













20





+100







20





+100



20




+100





Based on known facts (see below), it fails to compile because:



  • the type checker goes through the function in the order it was written,

  • in let example = Default::default();, example can be anything which implements Default,

  • field accesses & method calls require a known type,

  • "anything implementing Default" is not a known type.

I replaced some_method() with a field access and it produces same error.




From Type inference depends on ordering (#42333):




use std::path::PathBuf;

pub struct Thing
pub f1: PathBuf,


fn junk() -> Vec<Thing>
let mut things = Vec::new();
for x in vec![1, 2, 3]
if x == 2
for thing in things.drain(..)
thing.f1.clone();

return vec![]

things.push(Thingf1: PathBuf::from(format!("/", x)));

things


fn main()
junk();




This produces a compiler error with Rust 1.33.0:



error[E0282]: type annotations needed
--> src/main.rs:13:17
|
9 | let mut things = Vec::new();
| ---------- consider giving `things` a type
...
13 | thing.f1.clone();
| ^^^^^ cannot infer type
|
= note: type must be known at this point


You should focus on the following comments from eddyb (a well-known member of the the Rust language design team since May, 2016).



Comment #1:




This is a known limitation of the in-order type-checker. While inference flows freely, thing.f1.clone() is checked before things.push(Thing ...) so it isn't known that thing: Thing when you try to access the f1 field. We may in the future move away from this, but there are no immediate plans.




What's more important is comment #2:




What I mean is that the type-checker goes through the function in the order it was written. [...] Fields accesses and methods calls are simply not supported unless the type is already known.







share|improve this answer















Based on known facts (see below), it fails to compile because:



  • the type checker goes through the function in the order it was written,

  • in let example = Default::default();, example can be anything which implements Default,

  • field accesses & method calls require a known type,

  • "anything implementing Default" is not a known type.

I replaced some_method() with a field access and it produces same error.




From Type inference depends on ordering (#42333):




use std::path::PathBuf;

pub struct Thing
pub f1: PathBuf,


fn junk() -> Vec<Thing>
let mut things = Vec::new();
for x in vec![1, 2, 3]
if x == 2
for thing in things.drain(..)
thing.f1.clone();

return vec![]

things.push(Thingf1: PathBuf::from(format!("/", x)));

things


fn main()
junk();




This produces a compiler error with Rust 1.33.0:



error[E0282]: type annotations needed
--> src/main.rs:13:17
|
9 | let mut things = Vec::new();
| ---------- consider giving `things` a type
...
13 | thing.f1.clone();
| ^^^^^ cannot infer type
|
= note: type must be known at this point


You should focus on the following comments from eddyb (a well-known member of the the Rust language design team since May, 2016).



Comment #1:




This is a known limitation of the in-order type-checker. While inference flows freely, thing.f1.clone() is checked before things.push(Thing ...) so it isn't known that thing: Thing when you try to access the f1 field. We may in the future move away from this, but there are no immediate plans.




What's more important is comment #2:




What I mean is that the type-checker goes through the function in the order it was written. [...] Fields accesses and methods calls are simply not supported unless the type is already known.








share|improve this answer














share|improve this answer



share|improve this answer








edited Mar 31 at 14:20









Shepmaster

167k17360510




167k17360510










answered Mar 19 at 23:04









zrzkazrzka

10.2k33258




10.2k33258







  • 2





    I don't think we need proof that eddyb is a prominent member; just a note next his name when you give the first comments would be enough. I think this answer would be improved by removing the Proof and Temporary sections; you can comment on your own answers for work in progress stuff, etc... no need to scare people with a longer answer when 1/3 doesn't actually answer. // // // On another note: awesome digging!

    – Matthieu M.
    Mar 20 at 16:26






  • 1





    1) No official documentation for the in-order type checking (incl. Rust compiler docs) 2) Lot of other issues / PRs mention in-order with a note that it's a known limitation 3) It's covered by tests like this one 4) Recompiled rustc with debug assertions and matched the output with the source code. I can confirm that there's in-order type checking (but not the rustc expert, still may be wrong). Impossible to summarize my findings in a short answer (too long, complicated, so many paths).

    – zrzka
    Mar 26 at 10:19






  • 1





    Based on summary from my previous comment, I'm affraid that there's not much else to add. I'm happy with what I've found, learned some internals, but I also agree that this isn't satisfying / very good answer. It's probably all we can do now unless some rust compiler team member steps in. If you want to play with it, here're some notes from Niko describing the fastest way to get a working compiler (if you'd like to rebuild with debug assertions).

    – zrzka
    Mar 26 at 10:29






  • 2





    I personally find your answer very satisfying; sure it's not a "proof", but I don't think the Rust language is willing to enshrine type inference in the specification (as it leads to stagnation), so I suspect that studying the code and referencing known limitations is the best we can get.

    – Matthieu M.
    Mar 26 at 12:38






  • 1





    Happy 10K reputation!

    – Shepmaster
    Mar 31 at 14:13












  • 2





    I don't think we need proof that eddyb is a prominent member; just a note next his name when you give the first comments would be enough. I think this answer would be improved by removing the Proof and Temporary sections; you can comment on your own answers for work in progress stuff, etc... no need to scare people with a longer answer when 1/3 doesn't actually answer. // // // On another note: awesome digging!

    – Matthieu M.
    Mar 20 at 16:26






  • 1





    1) No official documentation for the in-order type checking (incl. Rust compiler docs) 2) Lot of other issues / PRs mention in-order with a note that it's a known limitation 3) It's covered by tests like this one 4) Recompiled rustc with debug assertions and matched the output with the source code. I can confirm that there's in-order type checking (but not the rustc expert, still may be wrong). Impossible to summarize my findings in a short answer (too long, complicated, so many paths).

    – zrzka
    Mar 26 at 10:19






  • 1





    Based on summary from my previous comment, I'm affraid that there's not much else to add. I'm happy with what I've found, learned some internals, but I also agree that this isn't satisfying / very good answer. It's probably all we can do now unless some rust compiler team member steps in. If you want to play with it, here're some notes from Niko describing the fastest way to get a working compiler (if you'd like to rebuild with debug assertions).

    – zrzka
    Mar 26 at 10:29






  • 2





    I personally find your answer very satisfying; sure it's not a "proof", but I don't think the Rust language is willing to enshrine type inference in the specification (as it leads to stagnation), so I suspect that studying the code and referencing known limitations is the best we can get.

    – Matthieu M.
    Mar 26 at 12:38






  • 1





    Happy 10K reputation!

    – Shepmaster
    Mar 31 at 14:13







2




2





I don't think we need proof that eddyb is a prominent member; just a note next his name when you give the first comments would be enough. I think this answer would be improved by removing the Proof and Temporary sections; you can comment on your own answers for work in progress stuff, etc... no need to scare people with a longer answer when 1/3 doesn't actually answer. // // // On another note: awesome digging!

– Matthieu M.
Mar 20 at 16:26





I don't think we need proof that eddyb is a prominent member; just a note next his name when you give the first comments would be enough. I think this answer would be improved by removing the Proof and Temporary sections; you can comment on your own answers for work in progress stuff, etc... no need to scare people with a longer answer when 1/3 doesn't actually answer. // // // On another note: awesome digging!

– Matthieu M.
Mar 20 at 16:26




1




1





1) No official documentation for the in-order type checking (incl. Rust compiler docs) 2) Lot of other issues / PRs mention in-order with a note that it's a known limitation 3) It's covered by tests like this one 4) Recompiled rustc with debug assertions and matched the output with the source code. I can confirm that there's in-order type checking (but not the rustc expert, still may be wrong). Impossible to summarize my findings in a short answer (too long, complicated, so many paths).

– zrzka
Mar 26 at 10:19





1) No official documentation for the in-order type checking (incl. Rust compiler docs) 2) Lot of other issues / PRs mention in-order with a note that it's a known limitation 3) It's covered by tests like this one 4) Recompiled rustc with debug assertions and matched the output with the source code. I can confirm that there's in-order type checking (but not the rustc expert, still may be wrong). Impossible to summarize my findings in a short answer (too long, complicated, so many paths).

– zrzka
Mar 26 at 10:19




1




1





Based on summary from my previous comment, I'm affraid that there's not much else to add. I'm happy with what I've found, learned some internals, but I also agree that this isn't satisfying / very good answer. It's probably all we can do now unless some rust compiler team member steps in. If you want to play with it, here're some notes from Niko describing the fastest way to get a working compiler (if you'd like to rebuild with debug assertions).

– zrzka
Mar 26 at 10:29





Based on summary from my previous comment, I'm affraid that there's not much else to add. I'm happy with what I've found, learned some internals, but I also agree that this isn't satisfying / very good answer. It's probably all we can do now unless some rust compiler team member steps in. If you want to play with it, here're some notes from Niko describing the fastest way to get a working compiler (if you'd like to rebuild with debug assertions).

– zrzka
Mar 26 at 10:29




2




2





I personally find your answer very satisfying; sure it's not a "proof", but I don't think the Rust language is willing to enshrine type inference in the specification (as it leads to stagnation), so I suspect that studying the code and referencing known limitations is the best we can get.

– Matthieu M.
Mar 26 at 12:38





I personally find your answer very satisfying; sure it's not a "proof", but I don't think the Rust language is willing to enshrine type inference in the specification (as it leads to stagnation), so I suspect that studying the code and referencing known limitations is the best we can get.

– Matthieu M.
Mar 26 at 12:38




1




1





Happy 10K reputation!

– Shepmaster
Mar 31 at 14:13





Happy 10K reputation!

– Shepmaster
Mar 31 at 14:13













8














I don't know the full answer and I have next to no knowledge of the internal workings of the Rust compiler, but here are some deductions I've come to from my experience with Rust.



Information about types in Rust can "flow backwards", but there are certain times when Rust needs to know (for absolute certain) the type of an expression. In these situations, it must "already" know the type, i.e. it will not continue to look forward.



From what I've seen, this situation is limited to method calls. I suspect it has something to do with the fact that methods can be implemented on traits, which substantially complicates things. I doubt that there are any traits in scope with a method named some_method, but I think that whenever the Rust compiler encounters a method call it requires the type to already be known for certain.



You can see this happen a lot with method calls on types which implement traits, the most common being the collect method on a type that implements the Iter trait. You will be able to call collect, but won't be able to call any methods on the result unless you specify the type.



So this works:



fn create_numbers(last_num: i32) -> Vec<i32> 
let x = (0..10).collect();
x



But this does not:



fn create_numbers(last_num: i32) -> Vec<i32> 
let x = (0..10).collect();
// In order to call `push`, we need to *already* know the type
// of x for "absolute certain", and the Rust compiler doesn't
// keep looking forward
x.push(42);
x






share|improve this answer























  • "I suspect it has something to do with the fact that methods can be implemented on traits" — I don't think it's specifically about the possibility of there being an ambiguous trait. I think it's just that the type must be known at the point of calling a method, in order to know which function is intended. It could be a trait but there could also be multiple types with this method in their impl.

    – Peter Hall
    Mar 19 at 16:29











  • I appreciate the response, but isn't a very satisfying answer. The second half of the post (starting at "You can see...") is simply a restatement of the problem in the question and doesn't provide an answer. The first half correlates with my experiences, but, as you point out, isn't based on any particular authoritative source.

    – Shepmaster
    Mar 19 at 16:41
















8














I don't know the full answer and I have next to no knowledge of the internal workings of the Rust compiler, but here are some deductions I've come to from my experience with Rust.



Information about types in Rust can "flow backwards", but there are certain times when Rust needs to know (for absolute certain) the type of an expression. In these situations, it must "already" know the type, i.e. it will not continue to look forward.



From what I've seen, this situation is limited to method calls. I suspect it has something to do with the fact that methods can be implemented on traits, which substantially complicates things. I doubt that there are any traits in scope with a method named some_method, but I think that whenever the Rust compiler encounters a method call it requires the type to already be known for certain.



You can see this happen a lot with method calls on types which implement traits, the most common being the collect method on a type that implements the Iter trait. You will be able to call collect, but won't be able to call any methods on the result unless you specify the type.



So this works:



fn create_numbers(last_num: i32) -> Vec<i32> 
let x = (0..10).collect();
x



But this does not:



fn create_numbers(last_num: i32) -> Vec<i32> 
let x = (0..10).collect();
// In order to call `push`, we need to *already* know the type
// of x for "absolute certain", and the Rust compiler doesn't
// keep looking forward
x.push(42);
x






share|improve this answer























  • "I suspect it has something to do with the fact that methods can be implemented on traits" — I don't think it's specifically about the possibility of there being an ambiguous trait. I think it's just that the type must be known at the point of calling a method, in order to know which function is intended. It could be a trait but there could also be multiple types with this method in their impl.

    – Peter Hall
    Mar 19 at 16:29











  • I appreciate the response, but isn't a very satisfying answer. The second half of the post (starting at "You can see...") is simply a restatement of the problem in the question and doesn't provide an answer. The first half correlates with my experiences, but, as you point out, isn't based on any particular authoritative source.

    – Shepmaster
    Mar 19 at 16:41














8












8








8







I don't know the full answer and I have next to no knowledge of the internal workings of the Rust compiler, but here are some deductions I've come to from my experience with Rust.



Information about types in Rust can "flow backwards", but there are certain times when Rust needs to know (for absolute certain) the type of an expression. In these situations, it must "already" know the type, i.e. it will not continue to look forward.



From what I've seen, this situation is limited to method calls. I suspect it has something to do with the fact that methods can be implemented on traits, which substantially complicates things. I doubt that there are any traits in scope with a method named some_method, but I think that whenever the Rust compiler encounters a method call it requires the type to already be known for certain.



You can see this happen a lot with method calls on types which implement traits, the most common being the collect method on a type that implements the Iter trait. You will be able to call collect, but won't be able to call any methods on the result unless you specify the type.



So this works:



fn create_numbers(last_num: i32) -> Vec<i32> 
let x = (0..10).collect();
x



But this does not:



fn create_numbers(last_num: i32) -> Vec<i32> 
let x = (0..10).collect();
// In order to call `push`, we need to *already* know the type
// of x for "absolute certain", and the Rust compiler doesn't
// keep looking forward
x.push(42);
x






share|improve this answer













I don't know the full answer and I have next to no knowledge of the internal workings of the Rust compiler, but here are some deductions I've come to from my experience with Rust.



Information about types in Rust can "flow backwards", but there are certain times when Rust needs to know (for absolute certain) the type of an expression. In these situations, it must "already" know the type, i.e. it will not continue to look forward.



From what I've seen, this situation is limited to method calls. I suspect it has something to do with the fact that methods can be implemented on traits, which substantially complicates things. I doubt that there are any traits in scope with a method named some_method, but I think that whenever the Rust compiler encounters a method call it requires the type to already be known for certain.



You can see this happen a lot with method calls on types which implement traits, the most common being the collect method on a type that implements the Iter trait. You will be able to call collect, but won't be able to call any methods on the result unless you specify the type.



So this works:



fn create_numbers(last_num: i32) -> Vec<i32> 
let x = (0..10).collect();
x



But this does not:



fn create_numbers(last_num: i32) -> Vec<i32> 
let x = (0..10).collect();
// In order to call `push`, we need to *already* know the type
// of x for "absolute certain", and the Rust compiler doesn't
// keep looking forward
x.push(42);
x







share|improve this answer












share|improve this answer



share|improve this answer










answered Mar 19 at 15:45









Harrison McHarrison Mc

18529




18529












  • "I suspect it has something to do with the fact that methods can be implemented on traits" — I don't think it's specifically about the possibility of there being an ambiguous trait. I think it's just that the type must be known at the point of calling a method, in order to know which function is intended. It could be a trait but there could also be multiple types with this method in their impl.

    – Peter Hall
    Mar 19 at 16:29











  • I appreciate the response, but isn't a very satisfying answer. The second half of the post (starting at "You can see...") is simply a restatement of the problem in the question and doesn't provide an answer. The first half correlates with my experiences, but, as you point out, isn't based on any particular authoritative source.

    – Shepmaster
    Mar 19 at 16:41


















  • "I suspect it has something to do with the fact that methods can be implemented on traits" — I don't think it's specifically about the possibility of there being an ambiguous trait. I think it's just that the type must be known at the point of calling a method, in order to know which function is intended. It could be a trait but there could also be multiple types with this method in their impl.

    – Peter Hall
    Mar 19 at 16:29











  • I appreciate the response, but isn't a very satisfying answer. The second half of the post (starting at "You can see...") is simply a restatement of the problem in the question and doesn't provide an answer. The first half correlates with my experiences, but, as you point out, isn't based on any particular authoritative source.

    – Shepmaster
    Mar 19 at 16:41

















"I suspect it has something to do with the fact that methods can be implemented on traits" — I don't think it's specifically about the possibility of there being an ambiguous trait. I think it's just that the type must be known at the point of calling a method, in order to know which function is intended. It could be a trait but there could also be multiple types with this method in their impl.

– Peter Hall
Mar 19 at 16:29





"I suspect it has something to do with the fact that methods can be implemented on traits" — I don't think it's specifically about the possibility of there being an ambiguous trait. I think it's just that the type must be known at the point of calling a method, in order to know which function is intended. It could be a trait but there could also be multiple types with this method in their impl.

– Peter Hall
Mar 19 at 16:29













I appreciate the response, but isn't a very satisfying answer. The second half of the post (starting at "You can see...") is simply a restatement of the problem in the question and doesn't provide an answer. The first half correlates with my experiences, but, as you point out, isn't based on any particular authoritative source.

– Shepmaster
Mar 19 at 16:41






I appreciate the response, but isn't a very satisfying answer. The second half of the post (starting at "You can see...") is simply a restatement of the problem in the question and doesn't provide an answer. The first half correlates with my experiences, but, as you point out, isn't based on any particular authoritative source.

– Shepmaster
Mar 19 at 16:41


















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55243660%2fwhy-does-calling-a-method-on-a-variable-prevent-rust-from-inferring-the-type-of%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Kamusi Yaliyomo Aina za kamusi | Muundo wa kamusi | Faida za kamusi | Dhima ya picha katika kamusi | Marejeo | Tazama pia | Viungo vya nje | UrambazajiKuhusu kamusiGo-SwahiliWiki-KamusiKamusi ya Kiswahili na Kiingerezakuihariri na kuongeza habari

Swift 4 - func physicsWorld not invoked on collision? The Next CEO of Stack OverflowHow to call Objective-C code from Swift#ifdef replacement in the Swift language@selector() in Swift?#pragma mark in Swift?Swift for loop: for index, element in array?dispatch_after - GCD in Swift?Swift Beta performance: sorting arraysSplit a String into an array in Swift?The use of Swift 3 @objc inference in Swift 4 mode is deprecated?How to optimize UITableViewCell, because my UITableView lags

Access current req object everywhere in Node.js ExpressWhy are global variables considered bad practice? (node.js)Using req & res across functionsHow do I get the path to the current script with Node.js?What is Node.js' Connect, Express and “middleware”?Node.js w/ express error handling in callbackHow to access the GET parameters after “?” in Express?Modify Node.js req object parametersAccess “app” variable inside of ExpressJS/ConnectJS middleware?Node.js Express app - request objectAngular Http Module considered middleware?Session variables in ExpressJSAdd properties to the req object in expressjs with Typescript