Skip to content

Bug in generic trait implementation? #3744

Closed

Description

@tav

There is a possible bug when you define a generic trait implementation within a different module but within the same file, e.g. given:

type byte = u8;

mod crypto {

// Hash is the trait implemented by all cryptographic hash functions.
pub trait Hash {
    fn block_size() -> int;
    fn digest() -> ~[byte];
    fn digest_size() -> int;
    fn reset();
    fn update(msg: &[byte]);
}

// HashUtil is a utility trait that is auto-implemented for all Hash
// implementations.
pub trait HashUtil {
    fn hexdigest() -> ~str;
    fn update(msg: &str);
}

impl <A: Hash> A: HashUtil {

    fn hexdigest() -> ~str {
        let mut d = ~"";
        for vec::each(self.digest()) |b| {
            d += fmt!("%02x", *b as uint)
        }
        return d;
    }

    fn update(msg: &str) {
        self.update(str::to_bytes(msg));
    }

}

}

mod sha256 {

use crypto::Hash;

const block_size: int = 64;
const digest_size: int = 32;

const i0: u32 = 0x6a09e667;
const i1: u32 = 0xbb67ae85;
const i2: u32 = 0x3c6ef372;
const i3: u32 = 0xa54ff53a;
const i4: u32 = 0x510e527f;
const i5: u32 = 0x9b05688c;
const i6: u32 = 0x1f83d9ab;
const i7: u32 = 0x5be0cd19;

const k: [u32 * 64] =
    [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
     0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
     0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
     0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
     0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
     0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
     0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
     0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
     0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
     0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
     0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];

struct sha256 {
    b: [mut byte * 64],
    h: [mut u32 * 8],
    mut l: u64,
    mut n: uint,
}

impl sha256 {

    #[inline(always)]
    fn compute_block() {
        let mut h0 = self.h[0];
        let mut h1 = self.h[1];
        let mut h2 = self.h[2];
        let mut h3 = self.h[3];
        let mut h4 = self.h[4];
        let mut h5 = self.h[5];
        let mut h6 = self.h[6];
        let mut h7 = self.h[7];
        self.h[0] = h0;
        self.h[1] = h1;
        self.h[2] = h2;
        self.h[3] = h3;
        self.h[4] = h4;
        self.h[5] = h5;
        self.h[6] = h6;
        self.h[7] = h7;
    }

}

impl sha256: Hash {

    fn block_size() -> int { block_size }

    fn digest() -> ~[byte] {
        ~[1, 2, 37]
    }

    fn digest_size() -> int { digest_size }

    fn reset() {
        self.h[0] = i0;
        self.h[1] = i1;
        self.h[2] = i2;
        self.h[3] = i3;
        self.h[4] = i4;
        self.h[5] = i5;
        self.h[6] = i6;
        self.h[7] = i7;
        self.l = 0;
        self.n = 0;
    }

    fn update(msg: &[byte]) {
        let mut l = msg.len();
        self.l += l as u64;
        if self.n > 0 {
            if l > 64 - self.n {
                l = 64 - self.n;
            }
        }
    }

}

pub fn new() -> sha256 {
    sha256{
        b: [mut 0, ..64],
        h: [mut i0, i1, i2, i3, i4, i5, i6, i7],
        l: 0,
        n: 0
    }
}

}

fn main() {
    io::println(fmt!("val: %?", sha256::new().block_size()));
    io::println(fmt!("val: %?", sha256::new().hexdigest()));
}

The compiler errors with:

error: attempted access of field `hexdigest` on type `sha256::sha256`, but no field or method with that name was found

If, however, you put a use crypto::*; statement before the main(), then everything seems to work. This behaviour feels like a bug since block_size() works and hexdigest() is defined within the same module...

Activity

  1. catamorphism commented on Oct 12, 2012

    @catamorphism
    Contributor

    Not sure what the bug is here? block_size works because you imported its containing trait, Hash, explicitly. hexdigest doesn't because you didn't import its containing trait, HashUtil. You could remove use crypto::*; and say use crypto::HashUtil to also get a program that compiles.

    But maybe I'm misunderstanding?

  2. tav commented on Oct 12, 2012

    @tav
    ContributorAuthor

    The Hash trait is only imported in the sha256 module. Yet it works without a use crypto::Hash; before the main().

    In contrast, it requires an explicit use crypto::HashUtil; before the main(). Surely this isn't the intended behaviour?

  3. catamorphism commented on Oct 12, 2012

    @catamorphism
    Contributor

    Sorry, I didn't read your code correctly at first. Are you saying that if you comment out the line in main with the call to hexdigest, it works without inserting a use crypto::*;?

  4. tav commented on Oct 12, 2012

    @tav
    ContributorAuthor

    Yes :)

  5. catamorphism commented on Oct 12, 2012

    @catamorphism
    Contributor

    Okay, weird; I'll try to reproduce when I get a moment.

  6. catamorphism commented on Oct 15, 2012

    @catamorphism
    Contributor

    This actually works as intended; you just need to use crypto::HashUtil in the scope where main is declared. But why don't you need to use crypto::Hash explicitly? Because there's an impl of Hash for sha256. Since the impl of HashUtil is generic, you have to use it explicitly.

    It still seems a little weird to me, though. Maybe @pcwalton could comment?

  7. catamorphism commented on Oct 16, 2012

    @catamorphism
    Contributor

    I'm just going to close this since I think it's intended behavior. If you think it should change, feel free to file an RFC bug :-)

  8. nikomatsakis commented on Oct 16, 2012

    @nikomatsakis
    Contributor

    I actually didn't think that was the intended behavior. I thought you had to import traits to invoke trait methods, and I didn't think it was relevant where the impl for the trait appeared. @pcwalton is this incorrect?

  9. nikomatsakis commented on Dec 4, 2012

    @nikomatsakis
    Contributor

    Seems like this is expected. Closing.

  10. added a commit that references this issue on Jul 29, 2024
  11. added a commit that references this issue on Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-resolveArea: Name/path resolution done by `rustc_resolve` specificallyA-trait-systemArea: Trait system

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions